<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>DEV Community: Terry Quispe Paniagua</title>
    <description>The latest articles on DEV Community by Terry Quispe Paniagua (@terry_cloud).</description>
    <link>https://dev.to/terry_cloud</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F3614371%2F7804f481-6b8c-44c0-ab00-9e93e561a4bf.jpg</url>
      <title>DEV Community: Terry Quispe Paniagua</title>
      <link>https://dev.to/terry_cloud</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/terry_cloud"/>
    <language>en</language>
    <item>
      <title>🛡️ AWS IAM Access Analyzer — Parte 4/4</title>
      <dc:creator>Terry Quispe Paniagua</dc:creator>
      <pubDate>Sun, 16 Aug 2026 19:22:47 +0000</pubDate>
      <link>https://dev.to/terry_cloud/aws-iam-access-analyzer-parte-44-ljc</link>
      <guid>https://dev.to/terry_cloud/aws-iam-access-analyzer-parte-44-ljc</guid>
      <description>&lt;p&gt;Después de probar &lt;strong&gt;External Access&lt;/strong&gt; e &lt;strong&gt;Internal Access&lt;/strong&gt;, me quedaba una tercera capacidad de IAM Access Analyzer por revisar: &lt;strong&gt;Unused Access&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;La pregunta que quería responder era bastante simple:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;¿Qué accesos sigo manteniendo aunque ya no se estén utilizando?&lt;/strong&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Pero había otra pregunta todavía más importante:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Si IAM ya muestra &lt;code&gt;Last activity&lt;/code&gt; y datos de &lt;code&gt;Last accessed&lt;/code&gt;, ¿por qué pagaría por otro analyzer?&lt;/strong&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Para responderlo configuré un analyzer real sobre una de mis cuentas, limité deliberadamente el scope para controlar el costo y lo dejé trabajando durante una semana.&lt;/p&gt;

&lt;p&gt;El resultado fueron &lt;strong&gt;18 findings sobre 17 principals&lt;/strong&gt;, incluyendo roles completos sin uso, una contraseña de consola y, probablemente lo más interesante, permisos sin utilizar dentro de principals que no necesariamente estaban completamente inactivos.&lt;/p&gt;




&lt;h2&gt;
  
  
  Antes del lab: External vs Internal vs Unused Access
&lt;/h2&gt;

&lt;p&gt;Las tres capacidades responden preguntas diferentes.&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Analyzer&lt;/th&gt;
&lt;th&gt;Pregunta principal&lt;/th&gt;
&lt;th&gt;Qué analiza&lt;/th&gt;
&lt;th&gt;Pricing&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;External Access&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;¿Quién puede acceder desde fuera de mi zona de confianza?&lt;/td&gt;
&lt;td&gt;Recursos y resource-based policies&lt;/td&gt;
&lt;td&gt;Sin cargo adicional&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Internal Access&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;¿Qué principals internos pueden acceder a recursos específicos?&lt;/td&gt;
&lt;td&gt;Permisos efectivos sobre recursos seleccionados&lt;/td&gt;
&lt;td&gt;Por recurso monitoreado/mes/región&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Unused Access&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;¿Qué roles, credenciales o permisos ya no se utilizan?&lt;/td&gt;
&lt;td&gt;IAM users y roles&lt;/td&gt;
&lt;td&gt;Por IAM user/role analizado/mes&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;AWS documenta estas tres capacidades por separado porque no realizan el mismo análisis. External busca exposición pública o cross-account; Internal calcula acceso interno a recursos seleccionados; Unused analiza actividad histórica de identidades y permisos.&lt;/p&gt;




&lt;h2&gt;
  
  
  El laboratorio
&lt;/h2&gt;

&lt;p&gt;No quería activar el analyzer sobre todo sin controlar primero el costo.&lt;/p&gt;

&lt;p&gt;La configuración quedó así:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Analyzer:       UnusedAccess-Lab
Type:           ACCOUNT_UNUSED_ACCESS
Scope:          Current account
Tracking period: 60 days

Exclusion tag:
UnusedAnalyzerExclude=true
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Antes de crearlo etiqueté &lt;strong&gt;30 roles&lt;/strong&gt; que no quería incluir en este experimento.&lt;/p&gt;

&lt;p&gt;Después de las exclusiones quedaron en scope:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;16 IAM roles
 1 IAM user
-----------
17 principals
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Los &lt;strong&gt;service-linked roles&lt;/strong&gt; tampoco forman parte del análisis. Son roles de IAM creados y administrados por servicios de AWS para ejecutar acciones en tu cuenta en nombre de ese servicio. Normalmente utilizan la ruta &lt;code&gt;/aws-service-role/&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;Unused Access Analyzer no los evalúa, por lo que tampoco cuentan dentro del número de roles analizados.&lt;/p&gt;

&lt;p&gt;El tracking period fue de &lt;strong&gt;60 días&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;Esto no significa que haya que crear el analyzer y esperar 60 días. IAM Access Analyzer utiliza la información de &lt;code&gt;last accessed&lt;/code&gt; que IAM ya mantiene, por lo que puede generar findings usando actividad anterior a la creación del analyzer.&lt;/p&gt;

&lt;p&gt;El período configurado funciona como el umbral para generar los findings: un role puede aparecer cuando lleva ese tiempo inactivo, mientras que un permiso, password o access key puede aparecer cuando supera ese período sin uso. Para los permisos, AWS especifica además que solo evalúa permisos de entidades IAM que hayan existido durante todo el tracking period.&lt;/p&gt;

&lt;p&gt;AWS permite configurar entre &lt;strong&gt;1 y 365 días&lt;/strong&gt;.&lt;/p&gt;




&lt;h2&gt;
  
  
  ¿Cuánto cuesta Unused Access Analyzer?
&lt;/h2&gt;

&lt;p&gt;Esta parte conviene entenderla &lt;strong&gt;antes&lt;/strong&gt; de activarlo sobre una organización completa.&lt;/p&gt;

&lt;p&gt;AWS cobra actualmente:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;$0.20 USD
por IAM user o IAM role analizado
por analyzer
por mes
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;No cobra por finding.&lt;/p&gt;

&lt;p&gt;En este laboratorio dejé 17 principals dentro del análisis:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;17 × $0.20 = $3.40 USD/mes
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Ese era el costo teórico del scope que había definido.&lt;/p&gt;

&lt;p&gt;Y esta diferencia es importante porque finalmente obtuve &lt;strong&gt;18 findings&lt;/strong&gt;, pero eso no significa:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;18 × $0.20
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;La unidad de cobro es el &lt;strong&gt;principal analizado&lt;/strong&gt;, no el número de findings que genera.&lt;/p&gt;

&lt;p&gt;AWS cobra los analyzers de Internal y Unused Access una vez durante su configuración y, después, el &lt;strong&gt;primer día de cada mes calendario&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;En este laboratorio activé &lt;code&gt;UnusedAccess-Lab&lt;/code&gt; el &lt;strong&gt;9 de agosto&lt;/strong&gt;, por lo que el análisis inicial se cobró ese día. El siguiente ciclo de cobro no ocurre 30 días después, el 9 de septiembre, sino el &lt;strong&gt;1 de septiembre&lt;/strong&gt; para los principals que continúen dentro del análisis.&lt;/p&gt;

&lt;p&gt;Por eso las exclusiones por tags pueden ser especialmente útiles. AWS permite excluir IAM users y roles mediante pares &lt;code&gt;key=value&lt;/code&gt;; esos principals dejan de generar findings dentro de ese analyzer.&lt;/p&gt;

&lt;p&gt;En mi caso utilicé:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;UnusedAnalyzerExclude=true
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;h2&gt;
  
  
  Después de una semana: 18 findings
&lt;/h2&gt;

&lt;p&gt;El resultado terminó así:&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Finding type&lt;/th&gt;
&lt;th&gt;Cantidad&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;UnusedIAMRole&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;14&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;UnusedIAMUserPassword&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;1&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;UnusedPermission&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;3&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;UnusedIAMUserAccessKey&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;0&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Total&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;18&lt;/strong&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;Los 18 findings corresponden a 17 principals porque un mismo principal puede generar más de un tipo de finding.&lt;/p&gt;

&lt;p&gt;Eso ocurrió precisamente con el IAM user &lt;code&gt;terraform&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fyhazgxfhy6lu8xnowufj.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fyhazgxfhy6lu8xnowufj.png" alt=" " width="799" height="378"&gt;&lt;/a&gt;&lt;/p&gt;




&lt;h2&gt;
  
  
  Finding 1: un role realmente abandonado
&lt;/h2&gt;

&lt;p&gt;Uno de los casos más simples fue:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Role:
ec2rolssm

Finding:
UnusedIAMRole

Last accessed:
2025-11-23 03:52:31 UTC
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Con un tracking period de 60 días, no había mucha ambigüedad: el role llevaba muchísimo más tiempo sin actividad.&lt;/p&gt;

&lt;p&gt;El analyzer generó:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;findingType: UnusedIAMRole
status: ACTIVE
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;y mantuvo también el timestamp de su último acceso.&lt;/p&gt;

&lt;p&gt;Este es probablemente el caso que cualquiera esperaría de un producto llamado &lt;em&gt;Unused Access&lt;/em&gt;: encontrar roles que siguen existiendo aunque aparentemente ya nadie los use.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fq4h9564h51ig3juiaqc6.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fq4h9564h51ig3juiaqc6.png" alt=" " width="800" height="340"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Pero este tampoco es el caso que justifica por sí solo pagar por el feature.&lt;/p&gt;

&lt;p&gt;Para detectar algo así, IAM ya tiene información de último uso.&lt;/p&gt;

&lt;p&gt;Lo interesante viene después.&lt;/p&gt;




&lt;h2&gt;
  
  
  Finding 2: también aparecieron roles de IAM Identity Center
&lt;/h2&gt;

&lt;p&gt;Entre los findings apareció:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;AWSReservedSSO_AWSAdministratorAccess_xxxxxxxxxxxxxxx
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;con:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Finding:
UnusedIAMRole

Last accessed:
2025-10-06 01:52:45 UTC
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Es un role &lt;code&gt;AWSReservedSSO_*&lt;/code&gt; generado por IAM Identity Center y también terminó identificado como unused.&lt;/p&gt;

&lt;p&gt;No fue el único. Varios de los &lt;code&gt;AWSReservedSSO_*&lt;/code&gt; que quedaron dentro del scope generaron findings.&lt;/p&gt;

&lt;p&gt;Hay además una limitación relacionada: si un role de IAM Identity Center genera un finding de tipo &lt;code&gt;UnusedPermission&lt;/code&gt;, AWS no soporta policy recommendations para ese tipo de role.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fncan83dza2hrj4h2u1wo.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fncan83dza2hrj4h2u1wo.png" alt=" " width="799" height="307"&gt;&lt;/a&gt;&lt;/p&gt;




&lt;h2&gt;
  
  
  Finding 3: una contraseña que sigue existiendo pero ya no se usa
&lt;/h2&gt;

&lt;p&gt;El único IAM user del experimento era:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;terraform
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;y generó:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;findingType:
UnusedIAMUserPassword

lastAccessed:
2026-05-03 15:50:22 UTC
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Es decir: la contraseña de consola seguía asociada al IAM user, pero había superado ampliamente nuestra ventana de 60 días sin uso.&lt;/p&gt;

&lt;p&gt;Este tipo de finding me parece más útil que revisar solamente roles.&lt;/p&gt;

&lt;p&gt;Unused Access Analyzer también puede detectar:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Unused IAM roles
Unused IAM user passwords
Unused IAM user access keys
Unused permissions
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;En este lab no apareció ninguna access key unused, así que no voy a fabricar un ejemplo que no ocurrió.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2F3gwb7inb4kbno5olcfgs.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2F3gwb7inb4kbno5olcfgs.png" alt=" " width="800" height="301"&gt;&lt;/a&gt;&lt;/p&gt;




&lt;h2&gt;
  
  
  Finding 4: acá está la diferencia real con &lt;code&gt;Last activity&lt;/code&gt;
&lt;/h2&gt;

&lt;p&gt;El mismo user &lt;code&gt;terraform&lt;/code&gt; generó además:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;findingType:
UnusedPermission
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Y este finding es mucho más interesante.&lt;/p&gt;

&lt;p&gt;El JSON devuelto por &lt;code&gt;GetFindingV2&lt;/code&gt; contenía &lt;strong&gt;453 bloques &lt;code&gt;unusedPermissionDetails&lt;/code&gt;&lt;/strong&gt; y, dentro de ellos, &lt;strong&gt;302 acciones individuales distribuidas en cinco servicios&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;Es decir, el problema no era simplemente:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;"este user nunca se usa"
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;El finding no estaba limitado a un solo servicio. Aparecían permisos no utilizados en una gran cantidad de servicios; entre ellos ACM, CloudFormation, IAM, Route 53 y muchos otros.&lt;/p&gt;

&lt;p&gt;Para mostrar el comportamiento sin convertir esta sección en una lista enorme, tomemos ACM como ejemplo.&lt;/p&gt;

&lt;p&gt;El finding registraba actividad reciente sobre el servicio:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;serviceNamespace:
acm

lastAccessed:
2026-08-12
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Pero dentro del mismo servicio aparecían acciones consideradas unused como:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;acm:RequestCertificate
acm:ExportCertificate
acm:RenewCertificate
acm:ImportCertificate
acm:DeleteCertificate
...
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Algo parecido ocurría con CloudFormation e IAM: el servicio podía tener actividad reciente mientras numerosas acciones concedidas seguían apareciendo dentro del análisis de permisos no utilizados.&lt;/p&gt;

&lt;p&gt;Y ahí está para mí el punto más fuerte del feature:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Que un principal esté activo no significa que necesite todos los permisos que tiene.&lt;/strong&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fi6woaq0zvmjr4ccrdthr.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fi6woaq0zvmjr4ccrdthr.png" alt=" " width="800" height="542"&gt;&lt;/a&gt;&lt;/p&gt;




&lt;h2&gt;
  
  
  Entonces, ¿qué aporta si IAM ya muestra &lt;code&gt;Last activity&lt;/code&gt;?
&lt;/h2&gt;

&lt;p&gt;IAM ya tiene esa información y no tiene sentido ignorarlo.&lt;/p&gt;

&lt;p&gt;Por ejemplo, &lt;code&gt;RoleLastUsed&lt;/code&gt; puede indicar:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Last used date
Region
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;para un role, con información disponible para los últimos &lt;strong&gt;400 días&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;IAM también dispone de datos de &lt;strong&gt;service last accessed&lt;/strong&gt; y, para servicios soportados, información a nivel de acción.&lt;/p&gt;

&lt;p&gt;De hecho, AWS documenta explícitamente que &lt;strong&gt;Unused Access Analyzer utiliza esa información de last accessed para generar sus findings&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;Así que Unused Access Analyzer no tiene una fuente mágica de telemetría diferente.&lt;/p&gt;

&lt;p&gt;La diferencia es operacional:&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;IAM&lt;/th&gt;
&lt;th&gt;Unused Access Analyzer&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Consulto un principal&lt;/td&gt;
&lt;td&gt;Analiza principals a escala de cuenta u organización&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Veo cuándo se usó un role&lt;/td&gt;
&lt;td&gt;Genera &lt;code&gt;UnusedIAMRole&lt;/code&gt;
&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Reviso manualmente credenciales&lt;/td&gt;
&lt;td&gt;Detecta password/access key unused&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Reviso Last Accessed&lt;/td&gt;
&lt;td&gt;Genera findings de permisos unused&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Investigación puntual&lt;/td&gt;
&lt;td&gt;Monitoreo continuo&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Datos&lt;/td&gt;
&lt;td&gt;Findings gestionables&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;La forma corta de decirlo sería:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;IAM te entrega la evidencia. Unused Access Analyzer convierte esa evidencia en un proceso continuo de detección.&lt;/strong&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Para cinco roles probablemente puedo hacer una revisión manual.&lt;/p&gt;

&lt;p&gt;Para cientos o miles de roles y users distribuidos por una AWS Organization, la conversación cambia (y el costo también).&lt;/p&gt;




&lt;h2&gt;
  
  
  Algo que observé: los findings no aparecieron todos al mismo tiempo
&lt;/h2&gt;

&lt;p&gt;El analyzer fue creado el:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;2026-08-09
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Los findings de roles y password aparecieron ese mismo día.&lt;/p&gt;

&lt;p&gt;Sin embargo, los tres findings de &lt;code&gt;UnusedPermission&lt;/code&gt; se crearon posteriormente, el:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;2026-08-13
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Cuatro días después.&lt;/p&gt;

&lt;p&gt;Y al revisar el JSON una semana después todos habían sido reanalizados nuevamente el 16 de agosto.&lt;/p&gt;

&lt;p&gt;AWS no proporciona un tiempo exacto para que todos los findings estén disponibles; su documentación simplemente advierte que después de crear o actualizar un analyzer puede tomar tiempo antes de que aparezcan.&lt;/p&gt;

&lt;p&gt;Para una prueba rápida puedes mirar los primeros findings casi inmediatamente, pero para una auditoría yo no asumiría que la primera pantalla representa necesariamente todo el análisis final.&lt;/p&gt;




&lt;h2&gt;
  
  
  Limitaciones que conviene conocer
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;1. &lt;code&gt;Unused&lt;/code&gt; no significa automáticamente &lt;code&gt;innecesario&lt;/code&gt;.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Un role de DR, una cuenta break-glass (acceso de emergencia) o un proceso trimestral puede pasar 60 días sin utilizarse y seguir siendo necesario.&lt;/p&gt;

&lt;p&gt;El tracking period tiene que corresponder con el ciclo real de uso de tus accesos.&lt;/p&gt;




&lt;p&gt;&lt;strong&gt;2. El detalle action-level tiene límites.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;AWS puede evaluar unused permissions a nivel de servicio ampliamente, pero el análisis a nivel de acción depende de los servicios y acciones para los que IAM dispone de action last accessed information.&lt;/p&gt;




&lt;p&gt;&lt;strong&gt;3. Los service-linked roles no se analizan.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;No generan findings de unused access y tampoco forman parte del total de roles analizados para pricing.&lt;/p&gt;




&lt;p&gt;&lt;strong&gt;4. No todos mis findings trajeron el mismo nivel de detalle.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;En el JSON hubo varios &lt;code&gt;UnusedIAMRole&lt;/code&gt; donde:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="nl"&gt;"unusedIamRoleDetails"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;sin un &lt;code&gt;lastAccessed&lt;/code&gt; explícito.&lt;/p&gt;

&lt;p&gt;Otros roles sí incluían la fecha.&lt;/p&gt;

&lt;p&gt;Por eso no asumiría que cada finding tendrá exactamente la misma evidencia disponible.&lt;/p&gt;




&lt;h2&gt;
  
  
  Del finding a una policy de mínimo privilegio
&lt;/h2&gt;

&lt;p&gt;Hasta aquí el analyzer ya había identificado qué permisos del principal no se estaban utilizando, pero el finding tenía otra funcionalidad interesante.&lt;/p&gt;

&lt;p&gt;Al seleccionar un servicio, la consola abre un panel lateral con las acciones que no se han utilizado. Por ejemplo, para &lt;strong&gt;Certificate Manager&lt;/strong&gt; aparecían &lt;strong&gt;14 acciones&lt;/strong&gt; sin uso.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fj6w7b0hvqhvq67z7kbhp.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fj6w7b0hvqhvq67z7kbhp.png" alt=" " width="356" height="769"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Esto permite bajar del nivel:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Certificate Manager tiene permisos no utilizados
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;a acciones concretas como:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;acm:DeleteCertificate
acm:ExportCertificate
acm:GetCertificate
acm:ImportCertificate
acm:RenewCertificate
acm:RequestCertificate
...
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Pero el analyzer no se queda únicamente en mostrar el finding.&lt;/p&gt;

&lt;p&gt;El IAM user &lt;code&gt;terraform&lt;/code&gt; tenía asociadas estas tres policies:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;backuppolicy
AdministratorAccess
putalternatecontact
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;En la sección &lt;strong&gt;Recommendations&lt;/strong&gt;, Access Analyzer evaluó esas policies y mostró:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Existing policy          Recommended policy
--------------------------------------------------------
backuppolicy             None
AdministratorAccess      AdministratorAccess-recommended
putalternatecontact      None
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2F9mo9dvr692bommopm2td.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2F9mo9dvr692bommopm2td.png" alt=" " width="796" height="97"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Para &lt;code&gt;backuppolicy&lt;/code&gt; y &lt;code&gt;putalternatecontact&lt;/code&gt; no propuso una policy de reemplazo; la recomendación es retirar esas policies existentes. Para &lt;code&gt;AdministratorAccess&lt;/code&gt;, en cambio, generó &lt;code&gt;AdministratorAccess-recommended&lt;/code&gt; como policy de reemplazo.&lt;/p&gt;

&lt;p&gt;Al abrir &lt;strong&gt;Preview policy&lt;/strong&gt;, la consola permite comparar lado a lado la policy existente &lt;code&gt;AdministratorAccess&lt;/code&gt;, que concede:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"Effect"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Allow"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"Action"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"*"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"Resource"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"*"&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;con la policy recomendada, que contiene un conjunto mucho más reducido de permisos.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fe7ge8a02vhtwk138pzce.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fe7ge8a02vhtwk138pzce.png" alt=" " width="800" height="307"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fqufdpq8b1im6elg7fve1.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fqufdpq8b1im6elg7fve1.png" alt=" " width="800" height="298"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Esta recomendación puede utilizarse para sustituir &lt;code&gt;AdministratorAccess&lt;/code&gt; por permisos más ajustados y aplicar el principio de mínimo privilegio.&lt;/p&gt;

&lt;p&gt;Importante: Access Analyzer genera la recomendación, pero no reemplaza automáticamente la policy. AWS indica que primero debes crear y adjuntar la policy recomendada y después retirar la policy existente.&lt;/p&gt;

&lt;p&gt;Aquí es donde Unused Access Analyzer aporta algo más que simplemente indicar cuándo se utilizó por última vez un principal: además de detectar permisos no utilizados, puede proponer cómo reducir las permissions policies existentes.&lt;/p&gt;

&lt;p&gt;La policy recomendada debe revisarse antes de aplicarla. Un permiso que no se utilizó durante el período analizado puede seguir siendo necesario para una tarea menos frecuente.&lt;/p&gt;




&lt;h2&gt;
  
  
  ¿Lo habilitaría en producción?
&lt;/h2&gt;

&lt;p&gt;Depende de la escala.&lt;/p&gt;

&lt;p&gt;Si solamente tengo unos pocos roles y quiero responder:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;¿Cuándo se utilizó este role por última vez?&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;IAM ya me da información suficiente para hacer esa revisión manual.&lt;/p&gt;

&lt;p&gt;Pero si quiero responder continuamente:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;¿Qué roles llevan demasiado tiempo sin utilizarse?

¿Qué IAM users conservan passwords o access keys abandonadas?

¿Qué principals siguen activos pero tienen permisos que nunca utilizan?

¿Dónde tengo oportunidades de reducir privilegios?
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;entonces Unused Access Analyzer sí agrega una capa operacional útil.&lt;/p&gt;

&lt;p&gt;En una organización grande no lo habilitaría sin pensar primero en el scope y en el costo.&lt;/p&gt;

&lt;p&gt;La tarifa parece pequeña:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;$0.20 / principal / mes
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;pero:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;100 principals   = $20/mes
1,000 principals = $200/mes
10,000 principals = $2,000/mes
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;por analyzer.&lt;/p&gt;

&lt;p&gt;Las exclusiones mediante tags existen precisamente para controlar qué principals quieres analizar. AWS incluso recomienda utilizar una estrategia de tagging y exclusiones como mecanismo de optimización de costos.&lt;/p&gt;




&lt;h2&gt;
  
  
  Conclusión
&lt;/h2&gt;

&lt;p&gt;Este laboratorio empezó con una duda bastante válida:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;¿Para qué pagar por Unused Access Analyzer si IAM ya muestra Last activity?&lt;/strong&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Después de revisar los findings, mi respuesta es que &lt;strong&gt;no compiten exactamente por lo mismo&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;Si quiero investigar un role concreto, IAM ya tiene información muy útil.&lt;/p&gt;

&lt;p&gt;Pero Unused Access Analyzer toma esa información y la convierte en un mecanismo continuo para encontrar roles abandonados, credenciales sin uso y, sobre todo, &lt;strong&gt;permisos sobrantes dentro de principals que todavía tienen actividad&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;En mi caso, encontrar roles que no se utilizaban desde hacía meses era esperado.&lt;/p&gt;

&lt;p&gt;Lo que aportó más información fue ver un principal con actividad sobre determinados servicios mientras seguía acumulando cientos de permisos que el analyzer identificaba como no utilizados.&lt;/p&gt;

&lt;p&gt;Ahí es donde el feature empieza a tener sentido para least privilege.&lt;/p&gt;

&lt;p&gt;Eso sí: &lt;strong&gt;unused no significa automáticamente removable&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;El finding es el inicio de la revisión, no la autorización para borrar permisos a ciegas.&lt;/p&gt;

&lt;p&gt;Y, como con los otros analyzers de esta serie, primero conviene tener claro qué pregunta quieres responder. External, Internal y Unused Access pueden vivir bajo IAM Access Analyzer, pero están mirando problemas de seguridad bastante diferentes.&lt;/p&gt;




&lt;h2&gt;
  
  
  Referencias
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://aws.amazon.com/iam/access-analyzer/pricing/" rel="noopener noreferrer"&gt;AWS IAM Access Analyzer — Pricing&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://docs.aws.amazon.com/IAM/latest/UserGuide/access-analyzer-create-unused.html" rel="noopener noreferrer"&gt;AWS IAM Access Analyzer — Create an unused access analyzer&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://docs.aws.amazon.com/IAM/latest/UserGuide/access-analyzer-concepts.html" rel="noopener noreferrer"&gt;AWS IAM Access Analyzer — Understand how findings work&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://docs.aws.amazon.com/IAM/latest/UserGuide/access-analyzer-findings.html" rel="noopener noreferrer"&gt;AWS IAM Access Analyzer — Findings&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://docs.aws.amazon.com/IAM/latest/UserGuide/access-analyzer-findings-remediate.html" rel="noopener noreferrer"&gt;AWS IAM Access Analyzer — Resolve findings&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://docs.aws.amazon.com/IAM/latest/UserGuide/access_policies_last-accessed.html" rel="noopener noreferrer"&gt;AWS IAM — Refine permissions using last accessed information&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://docs.aws.amazon.com/IAM/latest/UserGuide/access_policies_last-accessed-action-last-accessed.html" rel="noopener noreferrer"&gt;AWS IAM — Action last accessed services and actions&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://aws.amazon.com/blogs/security/monitoring-and-optimizing-the-cost-of-the-unused-access-analyzer-in-iam-access-analyzer/" rel="noopener noreferrer"&gt;AWS Security Blog — Monitoring and optimizing the cost of the unused access analyzer&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;h1&gt;
  
  
  AWS #IAM #AccessAnalyzer #CloudSecurity #LeastPrivilege #AWSOrganizations #DevSecOps #IAMSecurity
&lt;/h1&gt;

</description>
    </item>
    <item>
      <title>AWS IAM Access Analyzer Internal Access: Effective Permissions, SCPs, RCPs, and an Unexpected Finding</title>
      <dc:creator>Terry Quispe Paniagua</dc:creator>
      <pubDate>Sun, 26 Jul 2026 16:31:02 +0000</pubDate>
      <link>https://dev.to/terry_cloud/aws-iam-access-analyzer-internal-access-effective-permissions-scps-rcps-and-an-unexpected-3bij</link>
      <guid>https://dev.to/terry_cloud/aws-iam-access-analyzer-internal-access-effective-permissions-scps-rcps-and-an-unexpected-3bij</guid>
      <description>&lt;h2&gt;
  
  
  Real-world demo: effective permissions, SCPs, RCPs, and a comparison that did not go as expected
&lt;/h2&gt;




&lt;p&gt;In the previous part, I explained the problem: in environments with multiple accounts, roles, and layered policies, figuring out who actually has access to a resource is not something you can answer by opening the console and reviewing a single IAM policy.&lt;/p&gt;

&lt;p&gt;In this post, I go straight to the lab. I configured a scenario with two accounts inside the same AWS Organization, an S3 bucket in one account, several IAM roles in another, and multiple control layers affecting access: &lt;strong&gt;IAM&lt;/strong&gt;, a &lt;strong&gt;bucket policy&lt;/strong&gt;, an &lt;strong&gt;SCP&lt;/strong&gt;, an &lt;strong&gt;RCP&lt;/strong&gt;, and a &lt;strong&gt;permissions boundary&lt;/strong&gt; attached to the administrator role.&lt;/p&gt;

&lt;p&gt;The goal was not only to verify whether an action worked, but also to compare what was configured, what &lt;strong&gt;IAM Access Analyzer Internal Access&lt;/strong&gt; reported, and what actually happened when the APIs were called.&lt;/p&gt;




&lt;blockquote&gt;
&lt;p&gt;⚠️ &lt;strong&gt;Cost note before you begin&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;IAM Access Analyzer External Access has &lt;strong&gt;no additional charge&lt;/strong&gt;, but Internal Access Analyzer &lt;strong&gt;is a paid capability&lt;/strong&gt;. AWS charges &lt;strong&gt;$9.00 USD per monitored resource, per analyzer, per Region, per month&lt;/strong&gt;. This lab monitors only one S3 bucket, so the estimated cost was &lt;strong&gt;$9.00 USD/month&lt;/strong&gt; while the analyzer remained active. If you enable it across multiple production resources, the cost scales linearly: 10 resources = $90 USD/month, and 38 resources across 5 accounts = $342 USD/month, as shown in the example on the &lt;a href="https://aws.amazon.com/iam/access-analyzer/pricing/" rel="noopener noreferrer"&gt;official AWS pricing page&lt;/a&gt;.&lt;/p&gt;
&lt;/blockquote&gt;




&lt;h2&gt;
  
  
  Lab: validating &lt;strong&gt;cross-account access within the same AWS Organization&lt;/strong&gt; using IAM Access Analyzer Internal Access
&lt;/h2&gt;

&lt;p&gt;In this lab, I validated how IAM Access Analyzer Internal Access represents access from roles in one account to an S3 bucket located in another account within the same organization.&lt;/p&gt;

&lt;p&gt;The scenario started with two roles:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;&lt;code&gt;RoleReadOnly&lt;/code&gt;&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;code&gt;RoleAdmin&lt;/code&gt;&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;I then added two control roles to compare whether the way actions were declared affected the results:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;&lt;code&gt;RoleDiagnosticExplicitNoBoundary&lt;/code&gt;&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;code&gt;RoleAdminWildcardNoBoundary&lt;/code&gt;&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;I also added three controls:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;An &lt;strong&gt;SCP&lt;/strong&gt; to block &lt;code&gt;s3:DeleteObject&lt;/code&gt; and &lt;code&gt;s3:DeleteObjectVersion&lt;/code&gt;&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;An &lt;strong&gt;RCP&lt;/strong&gt; to block &lt;code&gt;s3:PutObject&lt;/code&gt;&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;A &lt;strong&gt;permissions boundary&lt;/strong&gt; attached to &lt;code&gt;RoleAdmin&lt;/code&gt;&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Architecture
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;AWS Organization
└── Lab OU
    ├── Account A — resource owner
    │   └── S3 bucket: demo-internal-aa-2026
    │
    └── Account B — principal owner
        ├── RoleReadOnly
        ├── RoleAdmin
        ├── RoleDiagnosticExplicitNoBoundary
        └── RoleAdminWildcardNoBoundary
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Account A contains the S3 bucket, while Account B contains the roles that attempt to access it. Both accounts are inside the same OU, and both the SCP and the RCP were attached to that OU. The SCP limits the actions available to principals in the accounts under the OU, while the RCP limits access to resources in those accounts. In this lab, the ARN defined in the RCP restricts the deny to the bucket in Account A. The permissions boundary is attached directly to &lt;code&gt;RoleAdmin&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fqrv2efookam767lmxtca.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fqrv2efookam767lmxtca.png" alt="Architecture of the lab with two AWS accounts inside the same OU" width="799" height="517"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;The image above represents the first phase of the lab, when I was working only with &lt;code&gt;RoleReadOnly&lt;/code&gt; and &lt;code&gt;RoleAdmin&lt;/code&gt;. The other two roles were added later to make the comparison more controlled.&lt;/p&gt;

&lt;h3&gt;
  
  
  Objective
&lt;/h3&gt;

&lt;p&gt;The lab validates seven things:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;&lt;p&gt;Which roles inside the organization appear as having access to the bucket.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Which actions the finding reports for each role.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;How an SCP blocks an action even when IAM and the bucket policy allow it.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;How an RCP blocks an action from the resource side.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;How a permissions boundary limits the maximum permissions of a role.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;What changes when actions are declared explicitly instead of using &lt;code&gt;s3:*&lt;/code&gt;.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Whether the finding matches the actual API execution results.&lt;/p&gt;&lt;/li&gt;
&lt;/ol&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Role&lt;/th&gt;
&lt;th&gt;Configured permissions&lt;/th&gt;
&lt;th&gt;Boundary&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;RoleReadOnly&lt;/td&gt;
&lt;td&gt;ListBucket and GetObject&lt;/td&gt;
&lt;td&gt;No&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;RoleAdmin&lt;/td&gt;
&lt;td&gt;s3:*&lt;/td&gt;
&lt;td&gt;Yes: limited to ListBucket, GetObject, PutObject, and DeleteObject&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;RoleAdminWildcardNoBoundary&lt;/td&gt;
&lt;td&gt;s3:*&lt;/td&gt;
&lt;td&gt;No&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;RoleDiagnosticExplicitNoBoundary&lt;/td&gt;
&lt;td&gt;ListBucket, GetBucketPolicy, GetObject, PutObject, and DeleteObject&lt;/td&gt;
&lt;td&gt;No&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;The &lt;strong&gt;SCP&lt;/strong&gt; blocks &lt;code&gt;s3:DeleteObject&lt;/code&gt; and &lt;code&gt;s3:DeleteObjectVersion&lt;/code&gt;.&lt;br&gt;&lt;br&gt;
The &lt;strong&gt;RCP&lt;/strong&gt; blocks &lt;code&gt;s3:PutObject&lt;/code&gt; on the lab bucket.&lt;/p&gt;


&lt;h3&gt;
  
  
  Step 1: Create the roles in Account B
&lt;/h3&gt;

&lt;p&gt;From CloudShell in Account B, define the environment variables:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;export &lt;/span&gt;&lt;span class="nv"&gt;ACCOUNT_B_ID&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"&amp;lt;ACCOUNT_B_ID&amp;gt;"&lt;/span&gt;
&lt;span class="nb"&gt;export &lt;/span&gt;&lt;span class="nv"&gt;BUCKET_NAME&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"demo-internal-aa-2026"&lt;/span&gt; &lt;span class="c"&gt;# example&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Validate the current identity:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;aws sts get-caller-identity
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Next, create the trust policy that allows the roles to be assumed from within Account B:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;cat&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;&lt;/span&gt; trust-account-b.json &lt;span class="o"&gt;&amp;lt;&amp;lt;&lt;/span&gt;&lt;span class="no"&gt;EOF&lt;/span&gt;&lt;span class="sh"&gt;
{
  "Version": "2012-10-17",
  "Statement": [
    {
      "Sid": "AllowAssumeRoleFromAccountB",
      "Effect": "Allow",
      "Principal": {
        "AWS": "arn:aws:iam::&lt;/span&gt;&lt;span class="nv"&gt;$ACCOUNT_B_ID&lt;/span&gt;&lt;span class="sh"&gt;:root"
      },
      "Action": "sts:AssumeRole"
    }
  ]
}
&lt;/span&gt;&lt;span class="no"&gt;EOF
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Create the four roles:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="k"&gt;for &lt;/span&gt;ROLE &lt;span class="k"&gt;in&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
  RoleReadOnly &lt;span class="se"&gt;\&lt;/span&gt;
  RoleAdmin &lt;span class="se"&gt;\&lt;/span&gt;
  RoleDiagnosticExplicitNoBoundary &lt;span class="se"&gt;\&lt;/span&gt;
  RoleAdminWildcardNoBoundary
&lt;span class="k"&gt;do
  &lt;/span&gt;aws iam create-role &lt;span class="se"&gt;\&lt;/span&gt;
    &lt;span class="nt"&gt;--role-name&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$ROLE&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
    &lt;span class="nt"&gt;--assume-role-policy-document&lt;/span&gt; file://trust-account-b.json
&lt;span class="k"&gt;done&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;h3&gt;
  
  
  Step 2: Assign IAM permissions to the roles
&lt;/h3&gt;

&lt;p&gt;&lt;strong&gt;RoleReadOnly&lt;/strong&gt; — can only list and read:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;cat&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;&lt;/span&gt; role-readonly-policy.json &lt;span class="o"&gt;&amp;lt;&amp;lt;&lt;/span&gt;&lt;span class="no"&gt;EOF&lt;/span&gt;&lt;span class="sh"&gt;
{
  "Version": "2012-10-17",
  "Statement": [
    {
      "Sid": "ListBucket",
      "Effect": "Allow",
      "Action": "s3:ListBucket",
      "Resource": "arn:aws:s3:::&lt;/span&gt;&lt;span class="nv"&gt;$BUCKET_NAME&lt;/span&gt;&lt;span class="sh"&gt;"
    },
    {
      "Sid": "ReadObjects",
      "Effect": "Allow",
      "Action": "s3:GetObject",
      "Resource": "arn:aws:s3:::&lt;/span&gt;&lt;span class="nv"&gt;$BUCKET_NAME&lt;/span&gt;&lt;span class="sh"&gt;/*"
    }
  ]
}
&lt;/span&gt;&lt;span class="no"&gt;EOF

&lt;/span&gt;aws iam put-role-policy &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--role-name&lt;/span&gt; RoleReadOnly &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--policy-name&lt;/span&gt; DemoBucketReadOnlyAccess &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--policy-document&lt;/span&gt; file://role-readonly-policy.json
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;RoleAdmin&lt;/strong&gt; — allows &lt;code&gt;s3:*&lt;/code&gt; on the bucket and its objects:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;cat&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;&lt;/span&gt; role-admin-policy.json &lt;span class="o"&gt;&amp;lt;&amp;lt;&lt;/span&gt;&lt;span class="no"&gt;EOF&lt;/span&gt;&lt;span class="sh"&gt;
{
  "Version": "2012-10-17",
  "Statement": [
    {
      "Sid": "AdminBucketAccess",
      "Effect": "Allow",
      "Action": "s3:*",
      "Resource": [
        "arn:aws:s3:::&lt;/span&gt;&lt;span class="nv"&gt;$BUCKET_NAME&lt;/span&gt;&lt;span class="sh"&gt;",
        "arn:aws:s3:::&lt;/span&gt;&lt;span class="nv"&gt;$BUCKET_NAME&lt;/span&gt;&lt;span class="sh"&gt;/*"
      ]
    }
  ]
}
&lt;/span&gt;&lt;span class="no"&gt;EOF

&lt;/span&gt;aws iam put-role-policy &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--role-name&lt;/span&gt; RoleAdmin &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--policy-name&lt;/span&gt; DemoBucketAdminAccess &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--policy-document&lt;/span&gt; file://role-admin-policy.json
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;RoleDiagnosticExplicitNoBoundary&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;This role explicitly declares the actions I wanted to compare:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;cat&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;&lt;/span&gt; role-diagnostic-explicit-policy.json &lt;span class="o"&gt;&amp;lt;&amp;lt;&lt;/span&gt;&lt;span class="no"&gt;EOF&lt;/span&gt;&lt;span class="sh"&gt;
{
  "Version": "2012-10-17",
  "Statement": [
    {
      "Sid": "DiagnosticBucketActions",
      "Effect": "Allow",
      "Action": [
        "s3:ListBucket",
        "s3:GetBucketPolicy"
      ],
      "Resource": "arn:aws:s3:::&lt;/span&gt;&lt;span class="nv"&gt;$BUCKET_NAME&lt;/span&gt;&lt;span class="sh"&gt;"
    },
    {
      "Sid": "DiagnosticObjectActions",
      "Effect": "Allow",
      "Action": [
        "s3:GetObject",
        "s3:PutObject",
        "s3:DeleteObject"
      ],
      "Resource": "arn:aws:s3:::&lt;/span&gt;&lt;span class="nv"&gt;$BUCKET_NAME&lt;/span&gt;&lt;span class="sh"&gt;/*"
    }
  ]
}
&lt;/span&gt;&lt;span class="no"&gt;EOF

&lt;/span&gt;aws iam put-role-policy &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--role-name&lt;/span&gt; RoleDiagnosticExplicitNoBoundary &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--policy-name&lt;/span&gt; DiagnosticExplicitNoBoundaryAccess &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--policy-document&lt;/span&gt; file://role-diagnostic-explicit-policy.json
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;RoleAdminWildcardNoBoundary&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;This role keeps the wildcard and has no boundary:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;cat&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;&lt;/span&gt; role-admin-wildcard-policy.json &lt;span class="o"&gt;&amp;lt;&amp;lt;&lt;/span&gt;&lt;span class="no"&gt;EOF&lt;/span&gt;&lt;span class="sh"&gt;
{
  "Version": "2012-10-17",
  "Statement": [
    {
      "Sid": "AdminWildcardBucketAccess",
      "Effect": "Allow",
      "Action": "s3:*",
      "Resource": [
        "arn:aws:s3:::&lt;/span&gt;&lt;span class="nv"&gt;$BUCKET_NAME&lt;/span&gt;&lt;span class="sh"&gt;",
        "arn:aws:s3:::&lt;/span&gt;&lt;span class="nv"&gt;$BUCKET_NAME&lt;/span&gt;&lt;span class="sh"&gt;/*"
      ]
    }
  ]
}
&lt;/span&gt;&lt;span class="no"&gt;EOF

&lt;/span&gt;aws iam put-role-policy &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--role-name&lt;/span&gt; RoleAdminWildcardNoBoundary &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--policy-name&lt;/span&gt; AdminWildcardNoBoundaryAccess &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--policy-document&lt;/span&gt; file://role-admin-wildcard-policy.json
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;h3&gt;
  
  
  Step 2.1: Add a permissions boundary to RoleAdmin
&lt;/h3&gt;

&lt;p&gt;In addition to its inline policy with &lt;code&gt;s3:*&lt;/code&gt;, &lt;code&gt;RoleAdmin&lt;/code&gt; has a permissions boundary.&lt;/p&gt;

&lt;p&gt;The purpose was not to replace the SCP or the RCP. I wanted to add a layer that explicitly defined the maximum set of actions available to the role.&lt;/p&gt;

&lt;p&gt;The boundary allows:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;&lt;code&gt;s3:ListBucket&lt;/code&gt;&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;code&gt;s3:GetObject&lt;/code&gt;&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;code&gt;s3:PutObject&lt;/code&gt;&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;code&gt;s3:DeleteObject&lt;/code&gt;&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;For example, it does not allow:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;&lt;code&gt;s3:GetBucketPolicy&lt;/code&gt;&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;code&gt;s3:GetBucketAcl&lt;/code&gt;&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;code&gt;s3:GetBucketVersioning&lt;/code&gt;&lt;br&gt;
&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;cat&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;&lt;/span&gt; role-admin-boundary.json &lt;span class="o"&gt;&amp;lt;&amp;lt;&lt;/span&gt;&lt;span class="no"&gt;EOF&lt;/span&gt;&lt;span class="sh"&gt;
{
  "Version": "2012-10-17",
  "Statement": [
    {
      "Sid": "ListBucket",
      "Effect": "Allow",
      "Action": "s3:ListBucket",
      "Resource": "arn:aws:s3:::&lt;/span&gt;&lt;span class="nv"&gt;$BUCKET_NAME&lt;/span&gt;&lt;span class="sh"&gt;"
    },
    {
      "Sid": "PowerUserObjects",
      "Effect": "Allow",
      "Action": [
        "s3:GetObject",
        "s3:PutObject",
        "s3:DeleteObject"
      ],
      "Resource": "arn:aws:s3:::&lt;/span&gt;&lt;span class="nv"&gt;$BUCKET_NAME&lt;/span&gt;&lt;span class="sh"&gt;/*"
    }
  ]
}
&lt;/span&gt;&lt;span class="no"&gt;EOF

&lt;/span&gt;aws iam create-policy &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--policy-name&lt;/span&gt; boundary-adminuser &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--policy-document&lt;/span&gt; file://role-admin-boundary.json

aws iam put-role-permissions-boundary &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--role-name&lt;/span&gt; RoleAdmin &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--permissions-boundary&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
    &lt;span class="s2"&gt;"arn:aws:iam::&lt;/span&gt;&lt;span class="nv"&gt;$ACCOUNT_B_ID&lt;/span&gt;&lt;span class="s2"&gt;:policy/boundary-adminuser"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Validation:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;aws iam get-role &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--role-name&lt;/span&gt; RoleAdmin &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--query&lt;/span&gt; &lt;span class="s1"&gt;'Role.PermissionsBoundary'&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--output&lt;/span&gt; json
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;h3&gt;
  
  
  Step 3: Configure the bucket policy in Account A
&lt;/h3&gt;

&lt;p&gt;From CloudShell in Account A:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;export &lt;/span&gt;&lt;span class="nv"&gt;ACCOUNT_B_ID&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"&amp;lt;ACCOUNT_B_ID&amp;gt;"&lt;/span&gt;
&lt;span class="nb"&gt;export &lt;/span&gt;&lt;span class="nv"&gt;BUCKET_NAME&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"demo-internal-aa-2026"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The bucket policy grants each role the same actions used in its identity policy:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;cat&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;&lt;/span&gt; bucket-policy.json &lt;span class="o"&gt;&amp;lt;&amp;lt;&lt;/span&gt;&lt;span class="no"&gt;EOF&lt;/span&gt;&lt;span class="sh"&gt;
{
  "Version": "2012-10-17",
  "Statement": [
    {
      "Sid": "AllowReadOnlyListBucket",
      "Effect": "Allow",
      "Principal": {
        "AWS": "arn:aws:iam::&lt;/span&gt;&lt;span class="nv"&gt;$ACCOUNT_B_ID&lt;/span&gt;&lt;span class="sh"&gt;:role/RoleReadOnly"
      },
      "Action": "s3:ListBucket",
      "Resource": "arn:aws:s3:::&lt;/span&gt;&lt;span class="nv"&gt;$BUCKET_NAME&lt;/span&gt;&lt;span class="sh"&gt;"
    },
    {
      "Sid": "AllowReadOnlyGetObject",
      "Effect": "Allow",
      "Principal": {
        "AWS": "arn:aws:iam::&lt;/span&gt;&lt;span class="nv"&gt;$ACCOUNT_B_ID&lt;/span&gt;&lt;span class="sh"&gt;:role/RoleReadOnly"
      },
      "Action": "s3:GetObject",
      "Resource": "arn:aws:s3:::&lt;/span&gt;&lt;span class="nv"&gt;$BUCKET_NAME&lt;/span&gt;&lt;span class="sh"&gt;/*"
    },
    {
      "Sid": "AllowAdminFullBucketAccess",
      "Effect": "Allow",
      "Principal": {
        "AWS": "arn:aws:iam::&lt;/span&gt;&lt;span class="nv"&gt;$ACCOUNT_B_ID&lt;/span&gt;&lt;span class="sh"&gt;:role/RoleAdmin"
      },
      "Action": "s3:*",
      "Resource": [
        "arn:aws:s3:::&lt;/span&gt;&lt;span class="nv"&gt;$BUCKET_NAME&lt;/span&gt;&lt;span class="sh"&gt;",
        "arn:aws:s3:::&lt;/span&gt;&lt;span class="nv"&gt;$BUCKET_NAME&lt;/span&gt;&lt;span class="sh"&gt;/*"
      ]
    },
    {
      "Sid": "AllowAdminWildcardNoBoundaryFullBucketAccess",
      "Effect": "Allow",
      "Principal": {
        "AWS": "arn:aws:iam::&lt;/span&gt;&lt;span class="nv"&gt;$ACCOUNT_B_ID&lt;/span&gt;&lt;span class="sh"&gt;:role/RoleAdminWildcardNoBoundary"
      },
      "Action": "s3:*",
      "Resource": [
        "arn:aws:s3:::&lt;/span&gt;&lt;span class="nv"&gt;$BUCKET_NAME&lt;/span&gt;&lt;span class="sh"&gt;",
        "arn:aws:s3:::&lt;/span&gt;&lt;span class="nv"&gt;$BUCKET_NAME&lt;/span&gt;&lt;span class="sh"&gt;/*"
      ]
    },
    {
      "Sid": "AllowDiagnosticExplicitNoBoundaryBucketAccess",
      "Effect": "Allow",
      "Principal": {
        "AWS": "arn:aws:iam::&lt;/span&gt;&lt;span class="nv"&gt;$ACCOUNT_B_ID&lt;/span&gt;&lt;span class="sh"&gt;:role/RoleDiagnosticExplicitNoBoundary"
      },
      "Action": [
        "s3:ListBucket",
        "s3:GetBucketPolicy"
      ],
      "Resource": "arn:aws:s3:::&lt;/span&gt;&lt;span class="nv"&gt;$BUCKET_NAME&lt;/span&gt;&lt;span class="sh"&gt;"
    },
    {
      "Sid": "AllowDiagnosticExplicitNoBoundaryObjectAccess",
      "Effect": "Allow",
      "Principal": {
        "AWS": "arn:aws:iam::&lt;/span&gt;&lt;span class="nv"&gt;$ACCOUNT_B_ID&lt;/span&gt;&lt;span class="sh"&gt;:role/RoleDiagnosticExplicitNoBoundary"
      },
      "Action": [
        "s3:GetObject",
        "s3:PutObject",
        "s3:DeleteObject"
      ],
      "Resource": "arn:aws:s3:::&lt;/span&gt;&lt;span class="nv"&gt;$BUCKET_NAME&lt;/span&gt;&lt;span class="sh"&gt;/*"
    }
  ]
}
&lt;/span&gt;&lt;span class="no"&gt;EOF

&lt;/span&gt;aws s3api put-bucket-policy &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--bucket&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$BUCKET_NAME&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--policy&lt;/span&gt; file://bucket-policy.json
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Validation:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;aws s3api get-bucket-policy &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--bucket&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$BUCKET_NAME&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--query&lt;/span&gt; Policy &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--output&lt;/span&gt; text |
jq &lt;span class="nb"&gt;.&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;blockquote&gt;
&lt;p&gt;The real account also contained an earlier entry for &lt;code&gt;RolePowerUser&lt;/code&gt;. I did not use it in the final comparison because the four roles above already covered the cases I needed to isolate.&lt;/p&gt;
&lt;/blockquote&gt;




&lt;h3&gt;
  
  
  Step 4: Create the SCP in AWS Organizations
&lt;/h3&gt;

&lt;p&gt;From the management account, or another account with AWS Organizations permissions, define the variables:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;export &lt;/span&gt;&lt;span class="nv"&gt;BUCKET_NAME&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"demo-internal-aa-2026"&lt;/span&gt; &lt;span class="c"&gt;# Bucket name set at the beginning&lt;/span&gt;
&lt;span class="nb"&gt;export &lt;/span&gt;&lt;span class="nv"&gt;OU_ID&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"&amp;lt;OU_ID&amp;gt;"&lt;/span&gt;
&lt;span class="nb"&gt;export &lt;/span&gt;&lt;span class="nv"&gt;SCP_NAME&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"DenyDeleteObjectDemoBucket"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The SCP denies object deletion on the lab bucket:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;cat&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;&lt;/span&gt; deny-delete-demo-bucket-scp.json &lt;span class="o"&gt;&amp;lt;&amp;lt;&lt;/span&gt;&lt;span class="no"&gt;EOF&lt;/span&gt;&lt;span class="sh"&gt;
{
  "Version": "2012-10-17",
  "Statement": [
    {
      "Sid": "DenyDeleteObjectOnDemoBucket",
      "Effect": "Deny",
      "Action": [
        "s3:DeleteObject",
        "s3:DeleteObjectVersion"
      ],
      "Resource": "arn:aws:s3:::&lt;/span&gt;&lt;span class="nv"&gt;$BUCKET_NAME&lt;/span&gt;&lt;span class="sh"&gt;/*"
    }
  ]
}
&lt;/span&gt;&lt;span class="no"&gt;EOF
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Create the SCP and attach it to the OU that contains both Account A and Account B:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nv"&gt;POLICY_ID&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="si"&gt;$(&lt;/span&gt;aws organizations create-policy &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--name&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$SCP_NAME&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--description&lt;/span&gt; &lt;span class="s2"&gt;"Deny object deletion on demo S3 bucket for IAM Access Analyzer lab"&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--type&lt;/span&gt; SERVICE_CONTROL_POLICY &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--content&lt;/span&gt; file://deny-delete-demo-bucket-scp.json &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--query&lt;/span&gt; &lt;span class="s2"&gt;"Policy.PolicySummary.Id"&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--output&lt;/span&gt; text&lt;span class="si"&gt;)&lt;/span&gt;

&lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$POLICY_ID&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;

aws organizations attach-policy &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--policy-id&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$POLICY_ID&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--target-id&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$OU_ID&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;To validate that it was attached:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;aws organizations list-policies-for-target &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--target-id&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$OU_ID&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--filter&lt;/span&gt; SERVICE_CONTROL_POLICY &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--output&lt;/span&gt; table
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;h3&gt;
  
  
  Step 4.1: Add an RCP for the bucket
&lt;/h3&gt;

&lt;p&gt;After testing the SCP, I extended the exercise with a &lt;strong&gt;Resource Control Policy (RCP)&lt;/strong&gt; to block &lt;code&gt;s3:PutObject&lt;/code&gt; directly on the lab bucket.&lt;/p&gt;

&lt;p&gt;The purpose of this phase was no longer only to observe a deny from the principal side, but also to compare what happens when the deny comes from the resource side.&lt;/p&gt;

&lt;p&gt;The RCP was configured as follows:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"Version"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"2012-10-17"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"Statement"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"Sid"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"DenyPutObjectOnLabBucket"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"Effect"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Deny"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"Principal"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"*"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"Action"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"s3:PutObject"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"Resource"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"arn:aws:s3:::demo-internal-aa-2026/*"&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;An important detail: in an RCP, the correct element is &lt;strong&gt;Principal&lt;/strong&gt; with an uppercase P, and the permitted value for this type of policy is &lt;code&gt;"*"&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;Create the policy and attach it to the &lt;strong&gt;OU&lt;/strong&gt; that contains both the bucket account and the role account:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;export &lt;/span&gt;&lt;span class="nv"&gt;OU_ID&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"&amp;lt;OU_ID&amp;gt;"&lt;/span&gt;

&lt;span class="nb"&gt;cat&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;&lt;/span&gt; deny-putobject-rcp.json &lt;span class="o"&gt;&amp;lt;&amp;lt;&lt;/span&gt;&lt;span class="no"&gt;EOF&lt;/span&gt;&lt;span class="sh"&gt;
{
  "Version": "2012-10-17",
  "Statement": [
    {
      "Sid": "DenyPutObjectOnLabBucket",
      "Effect": "Deny",
      "Principal": "*",
      "Action": "s3:PutObject",
      "Resource": "arn:aws:s3:::demo-internal-aa-2026/*"
    }
  ]
}
&lt;/span&gt;&lt;span class="no"&gt;EOF

&lt;/span&gt;&lt;span class="nv"&gt;RCP_ID&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="si"&gt;$(&lt;/span&gt;aws organizations create-policy &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--name&lt;/span&gt; &lt;span class="s2"&gt;"DenyPutObjectOnLabBucketRCP"&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--description&lt;/span&gt; &lt;span class="s2"&gt;"RCP to deny PutObject on the lab bucket"&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--type&lt;/span&gt; RESOURCE_CONTROL_POLICY &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--content&lt;/span&gt; file://deny-putobject-rcp.json &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--query&lt;/span&gt; &lt;span class="s2"&gt;"Policy.PolicySummary.Id"&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--output&lt;/span&gt; text&lt;span class="si"&gt;)&lt;/span&gt;

&lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$RCP_ID&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;

aws organizations attach-policy &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--policy-id&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$RCP_ID&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--target-id&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$OU_ID&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;To validate that it was attached:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;aws organizations list-policies-for-target &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--target-id&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$OU_ID&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--filter&lt;/span&gt; RESOURCE_CONTROL_POLICY &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--output&lt;/span&gt; table
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;At this point, the lab looked like this:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;SCP&lt;/strong&gt; → blocks &lt;code&gt;DeleteObject&lt;/code&gt; and &lt;code&gt;DeleteObjectVersion&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;RCP&lt;/strong&gt; → blocks &lt;code&gt;PutObject&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Permissions boundary&lt;/strong&gt; → limits the maximum permissions of &lt;code&gt;RoleAdmin&lt;/code&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;This created a useful comparison because I was no longer testing a single organizational control layer, but several of them at the same time.&lt;/p&gt;




&lt;h3&gt;
  
  
  Step 5: Create the IAM Access Analyzer Internal Access analyzer
&lt;/h3&gt;

&lt;p&gt;From the console:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;IAM &amp;gt; Access Analyzer &amp;gt; Analyzer settings &amp;gt; Create analyzer
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Configuration:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Analysis:      Resource analysis - Internal access
Zone of trust: Entire organization
Resource:      arn:aws:s3:::demo-internal-aa-2026
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;An important detail: for S3, the resource must be the bucket ARN.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Correct:&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;arn:aws:s3:::demo-internal-aa-2026
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Object ARNs with prefixes or wildcards are not supported as monitored resources in Internal Access Analyzer. Only the bucket ARN without a path is supported.&lt;/p&gt;

&lt;p&gt;After creating the analyzer, wait until the first findings appear. For the final comparison, allow enough time for reanalysis, as explained later in the article.&lt;/p&gt;




&lt;h2&gt;
  
  
  Before testing: a quick summary of what each layer does
&lt;/h2&gt;

&lt;p&gt;If you read the previous post, this table provides a quick summary of the scope of each mechanism in this lab:&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Layer&lt;/th&gt;
&lt;th&gt;What it controls&lt;/th&gt;
&lt;th&gt;Main scope&lt;/th&gt;
&lt;th&gt;In this lab&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;IAM policy&lt;/td&gt;
&lt;td&gt;What the user or role can attempt to do&lt;/td&gt;
&lt;td&gt;Principal&lt;/td&gt;
&lt;td&gt;Defines the base permissions of the four roles&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Bucket policy&lt;/td&gt;
&lt;td&gt;Which principals can access the bucket&lt;/td&gt;
&lt;td&gt;Resource&lt;/td&gt;
&lt;td&gt;Enables cross-account access from Account B&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;SCP&lt;/td&gt;
&lt;td&gt;Maximum permissions available to principals in member accounts&lt;/td&gt;
&lt;td&gt;Account / OU / organization&lt;/td&gt;
&lt;td&gt;Blocks &lt;code&gt;DeleteObject&lt;/code&gt; and &lt;code&gt;DeleteObjectVersion&lt;/code&gt;
&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;RCP&lt;/td&gt;
&lt;td&gt;Maximum permissions available on resources in member accounts&lt;/td&gt;
&lt;td&gt;Resource in member account / OU / organization&lt;/td&gt;
&lt;td&gt;Blocks &lt;code&gt;PutObject&lt;/code&gt; on the bucket&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Permissions boundary&lt;/td&gt;
&lt;td&gt;Maximum permissions an IAM principal can have&lt;/td&gt;
&lt;td&gt;IAM principal&lt;/td&gt;
&lt;td&gt;Limits &lt;code&gt;RoleAdmin&lt;/code&gt;; does not allow &lt;code&gt;GetBucketPolicy&lt;/code&gt;
&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;A concise way to read it is:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;IAM&lt;/strong&gt; defines what the role attempts to do&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;The &lt;strong&gt;bucket policy&lt;/strong&gt; opens the cross-account access path&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;The &lt;strong&gt;SCP&lt;/strong&gt; limits permissions from the principal side&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;The &lt;strong&gt;RCP&lt;/strong&gt; limits permissions from the resource side&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;The &lt;strong&gt;boundary&lt;/strong&gt; limits the principal's maximum permissions&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;




&lt;h3&gt;
  
  
  Step 6: Test access by assuming the roles
&lt;/h3&gt;

&lt;p&gt;From CloudShell in Account B:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;export &lt;/span&gt;&lt;span class="nv"&gt;ACCOUNT_B_ID&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"&amp;lt;ACCOUNT_B_ID&amp;gt;"&lt;/span&gt;
&lt;span class="nb"&gt;export &lt;/span&gt;&lt;span class="nv"&gt;BUCKET_NAME&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"demo-internal-aa-2026"&lt;/span&gt;
&lt;span class="nb"&gt;export &lt;/span&gt;&lt;span class="nv"&gt;PREFIX&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"aa-lab"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Define functions to manage credentials and assume roles more easily:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;clear_role&lt;span class="o"&gt;()&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
  &lt;span class="nb"&gt;unset &lt;/span&gt;AWS_ACCESS_KEY_ID
  &lt;span class="nb"&gt;unset &lt;/span&gt;AWS_SECRET_ACCESS_KEY
  &lt;span class="nb"&gt;unset &lt;/span&gt;AWS_SESSION_TOKEN
  &lt;span class="nb"&gt;unset &lt;/span&gt;AWS_SECURITY_TOKEN
&lt;span class="o"&gt;}&lt;/span&gt;

assume_role&lt;span class="o"&gt;()&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
  &lt;span class="nv"&gt;ROLE_NAME&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$1&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;
  clear_role
  &lt;span class="nv"&gt;CREDS&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="si"&gt;$(&lt;/span&gt;aws sts assume-role &lt;span class="se"&gt;\&lt;/span&gt;
    &lt;span class="nt"&gt;--role-arn&lt;/span&gt; &lt;span class="s2"&gt;"arn:aws:iam::&lt;/span&gt;&lt;span class="nv"&gt;$ACCOUNT_B_ID&lt;/span&gt;&lt;span class="s2"&gt;:role/&lt;/span&gt;&lt;span class="nv"&gt;$ROLE_NAME&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
    &lt;span class="nt"&gt;--role-session-name&lt;/span&gt; &lt;span class="s2"&gt;"lab-&lt;/span&gt;&lt;span class="nv"&gt;$ROLE_NAME&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
    &lt;span class="nt"&gt;--output&lt;/span&gt; json&lt;span class="si"&gt;)&lt;/span&gt;
  &lt;span class="nb"&gt;export &lt;/span&gt;&lt;span class="nv"&gt;AWS_ACCESS_KEY_ID&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="si"&gt;$(&lt;/span&gt;&lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$CREDS&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt; | jq &lt;span class="nt"&gt;-r&lt;/span&gt; &lt;span class="s1"&gt;'.Credentials.AccessKeyId'&lt;/span&gt;&lt;span class="si"&gt;)&lt;/span&gt;
  &lt;span class="nb"&gt;export &lt;/span&gt;&lt;span class="nv"&gt;AWS_SECRET_ACCESS_KEY&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="si"&gt;$(&lt;/span&gt;&lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$CREDS&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt; | jq &lt;span class="nt"&gt;-r&lt;/span&gt; &lt;span class="s1"&gt;'.Credentials.SecretAccessKey'&lt;/span&gt;&lt;span class="si"&gt;)&lt;/span&gt;
  &lt;span class="nb"&gt;export &lt;/span&gt;&lt;span class="nv"&gt;AWS_SESSION_TOKEN&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="si"&gt;$(&lt;/span&gt;&lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$CREDS&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt; | jq &lt;span class="nt"&gt;-r&lt;/span&gt; &lt;span class="s1"&gt;'.Credentials.SessionToken'&lt;/span&gt;&lt;span class="si"&gt;)&lt;/span&gt;
  &lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"Assuming role: &lt;/span&gt;&lt;span class="nv"&gt;$ROLE_NAME&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;
  aws sts get-caller-identity
&lt;span class="o"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h4&gt;
  
  
  Test with &lt;code&gt;RoleReadOnly&lt;/code&gt;
&lt;/h4&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;assume_role RoleReadOnly
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;





&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;aws s3 &lt;span class="nb"&gt;ls&lt;/span&gt; &lt;span class="s2"&gt;"s3://&lt;/span&gt;&lt;span class="nv"&gt;$BUCKET_NAME&lt;/span&gt;&lt;span class="s2"&gt;/&lt;/span&gt;&lt;span class="nv"&gt;$PREFIX&lt;/span&gt;&lt;span class="s2"&gt;/"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;





&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;aws s3 &lt;span class="nb"&gt;cp&lt;/span&gt; &lt;span class="s2"&gt;"s3://&lt;/span&gt;&lt;span class="nv"&gt;$BUCKET_NAME&lt;/span&gt;&lt;span class="s2"&gt;/&lt;/span&gt;&lt;span class="nv"&gt;$PREFIX&lt;/span&gt;&lt;span class="s2"&gt;/seed.txt"&lt;/span&gt; readonly-seed.txt
&lt;span class="nb"&gt;cat &lt;/span&gt;readonly-seed.txt
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;





&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"test readonly put"&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;&lt;/span&gt; readonly-put.txt
aws s3 &lt;span class="nb"&gt;cp &lt;/span&gt;readonly-put.txt &lt;span class="s2"&gt;"s3://&lt;/span&gt;&lt;span class="nv"&gt;$BUCKET_NAME&lt;/span&gt;&lt;span class="s2"&gt;/&lt;/span&gt;&lt;span class="nv"&gt;$PREFIX&lt;/span&gt;&lt;span class="s2"&gt;/readonly-put.txt"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Observed result:&lt;/strong&gt; &lt;code&gt;PutObject&lt;/code&gt; fails with &lt;strong&gt;AccessDenied&lt;/strong&gt; because of the &lt;strong&gt;RCP&lt;/strong&gt;.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;aws s3 &lt;span class="nb"&gt;rm&lt;/span&gt; &lt;span class="s2"&gt;"s3://&lt;/span&gt;&lt;span class="nv"&gt;$BUCKET_NAME&lt;/span&gt;&lt;span class="s2"&gt;/&lt;/span&gt;&lt;span class="nv"&gt;$PREFIX&lt;/span&gt;&lt;span class="s2"&gt;/seed.txt"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Observed result:&lt;/strong&gt; &lt;code&gt;DeleteObject&lt;/code&gt; fails with &lt;strong&gt;AccessDenied&lt;/strong&gt; because of the &lt;strong&gt;IAM identity-based policy&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fut8yi7r1dto2ivzbpg9b.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fut8yi7r1dto2ivzbpg9b.png" alt="RoleReadOnly runtime results" width="798" height="137"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h4&gt;
  
  
  Test with &lt;code&gt;RoleAdmin&lt;/code&gt;
&lt;/h4&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;assume_role RoleAdmin
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;





&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;aws s3 &lt;span class="nb"&gt;ls&lt;/span&gt; &lt;span class="s2"&gt;"s3://&lt;/span&gt;&lt;span class="nv"&gt;$BUCKET_NAME&lt;/span&gt;&lt;span class="s2"&gt;/&lt;/span&gt;&lt;span class="nv"&gt;$PREFIX&lt;/span&gt;&lt;span class="s2"&gt;/"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;





&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;aws s3 &lt;span class="nb"&gt;cp&lt;/span&gt; &lt;span class="s2"&gt;"s3://&lt;/span&gt;&lt;span class="nv"&gt;$BUCKET_NAME&lt;/span&gt;&lt;span class="s2"&gt;/&lt;/span&gt;&lt;span class="nv"&gt;$PREFIX&lt;/span&gt;&lt;span class="s2"&gt;/seed.txt"&lt;/span&gt; admin-seed.txt
&lt;span class="nb"&gt;cat &lt;/span&gt;admin-seed.txt
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;





&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"test admin put"&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;&lt;/span&gt; admin-put.txt
aws s3 &lt;span class="nb"&gt;cp &lt;/span&gt;admin-put.txt &lt;span class="s2"&gt;"s3://&lt;/span&gt;&lt;span class="nv"&gt;$BUCKET_NAME&lt;/span&gt;&lt;span class="s2"&gt;/&lt;/span&gt;&lt;span class="nv"&gt;$PREFIX&lt;/span&gt;&lt;span class="s2"&gt;/admin-put.txt"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Observed result:&lt;/strong&gt; &lt;code&gt;PutObject&lt;/code&gt; fails with &lt;strong&gt;AccessDenied&lt;/strong&gt; because of the &lt;strong&gt;RCP&lt;/strong&gt;.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;aws s3 &lt;span class="nb"&gt;rm&lt;/span&gt; &lt;span class="s2"&gt;"s3://&lt;/span&gt;&lt;span class="nv"&gt;$BUCKET_NAME&lt;/span&gt;&lt;span class="s2"&gt;/&lt;/span&gt;&lt;span class="nv"&gt;$PREFIX&lt;/span&gt;&lt;span class="s2"&gt;/seed.txt"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Observed result:&lt;/strong&gt; &lt;code&gt;DeleteObject&lt;/code&gt; fails with &lt;strong&gt;AccessDenied&lt;/strong&gt; because of the &lt;strong&gt;SCP&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2F64evzaonbkryk3d6f9yp.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2F64evzaonbkryk3d6f9yp.png" alt="RoleAdmin runtime results" width="797" height="136"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h4&gt;
  
  
  Specific test to validate the permissions boundary
&lt;/h4&gt;

&lt;p&gt;To check how the boundary appeared in the denial message, I executed an action that:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;RoleAdmin&lt;/code&gt; would otherwise have through &lt;code&gt;s3:*&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;The bucket policy also allows&lt;/li&gt;
&lt;li&gt;Was not being blocked by the SCP or the RCP&lt;/li&gt;
&lt;li&gt;But the &lt;strong&gt;permissions boundary does not allow&lt;/strong&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The selected action was:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;assume_role RoleAdmin
aws s3api get-bucket-policy &lt;span class="nt"&gt;--bucket&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$BUCKET_NAME&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This produced the missing evidence: the &lt;code&gt;AccessDenied&lt;/code&gt; message states that &lt;code&gt;RoleAdmin&lt;/code&gt; &lt;strong&gt;is not authorized to perform&lt;/strong&gt; &lt;code&gt;s3:GetBucketPolicy&lt;/code&gt; because &lt;strong&gt;no permissions boundary allows that action&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fum5bdiyc4qycyvijnak9.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fum5bdiyc4qycyvijnak9.png" alt="AccessDenied message identifying the permissions boundary" width="800" height="25"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;The &lt;code&gt;AccessDenied&lt;/code&gt; message explicitly stated that no permissions boundary allowed &lt;code&gt;s3:GetBucketPolicy&lt;/code&gt;, confirming that the boundary participated in the evaluation.&lt;/p&gt;

&lt;h4&gt;
  
  
  Additional tests: explicit actions compared with &lt;code&gt;s3:*&lt;/code&gt;
&lt;/h4&gt;

&lt;p&gt;To better isolate the behavior of the finding, I added two roles without permissions boundaries:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;RoleDiagnosticExplicitNoBoundary&lt;/code&gt;, with the actions declared individually.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;RoleAdminWildcardNoBoundary&lt;/code&gt;, with &lt;code&gt;s3:*&lt;/code&gt;.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Both roles had access granted by their identity policies and the bucket policy, and both were subject to the same SCP and RCP.&lt;/p&gt;

&lt;p&gt;&lt;code&gt;RoleDiagnosticExplicitNoBoundary&lt;/code&gt; was allowed to perform &lt;code&gt;GetBucketPolicy&lt;/code&gt;, &lt;code&gt;GetObject&lt;/code&gt;, &lt;code&gt;PutObject&lt;/code&gt;, &lt;code&gt;DeleteObject&lt;/code&gt;, and &lt;code&gt;ListBucket&lt;/code&gt;. However, its finding showed only:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;code&gt;s3:GetBucketPolicy&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;s3:GetObject&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;s3:ListBucket&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;In other words, &lt;code&gt;PutObject&lt;/code&gt; and &lt;code&gt;DeleteObject&lt;/code&gt; did not appear, which matched what I expected after applying the RCP and the SCP.&lt;/p&gt;

&lt;p&gt;By contrast, &lt;code&gt;RoleAdminWildcardNoBoundary&lt;/code&gt; had &lt;code&gt;s3:*&lt;/code&gt;, and its finding retained a broad list of actions, including:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;code&gt;s3:PutObject&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;s3:DeleteObject&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;s3:DeleteObjectVersion&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;This happened even though the same finding showed the RCP and SCP restrictions as &lt;code&gt;APPLIED&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2F77ies7b61rm2lxqgdcr9.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2F77ies7b61rm2lxqgdcr9.png" alt="RoleAdminWildcardNoBoundary finding details" width="800" height="386"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fn3ni24hnddukk6mpzfmy.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fn3ni24hnddukk6mpzfmy.png" alt="RCP restriction shown as applied" width="355" height="300"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fk3u3thnpnf5w3poe7xnt.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fk3u3thnpnf5w3poe7xnt.png" alt="SCP restriction shown as applied" width="359" height="335"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;The screenshots show that the finding for &lt;code&gt;RoleAdminWildcardNoBoundary&lt;/code&gt; retains &lt;code&gt;PutObject&lt;/code&gt;, &lt;code&gt;DeleteObject&lt;/code&gt;, and &lt;code&gt;DeleteObjectVersion&lt;/code&gt;, even though the SCP and RCP restrictions appear as applied.&lt;/p&gt;

&lt;p&gt;To validate the actual access, I assumed both roles and ran the four main operations.&lt;/p&gt;

&lt;p&gt;In both cases:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;ListBucket&lt;/code&gt; and &lt;code&gt;GetObject&lt;/code&gt; were allowed.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;PutObject&lt;/code&gt; returned &lt;code&gt;AccessDenied&lt;/code&gt; because of an explicit deny in the RCP.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;DeleteObject&lt;/code&gt; returned &lt;code&gt;AccessDenied&lt;/code&gt; because of an explicit deny in the SCP.&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Role&lt;/th&gt;
&lt;th&gt;Permission declaration&lt;/th&gt;
&lt;th&gt;Expected result&lt;/th&gt;
&lt;th&gt;Observed finding&lt;/th&gt;
&lt;th&gt;Runtime&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;RoleDiagnosticExplicitNoBoundary&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Explicit actions&lt;/td&gt;
&lt;td&gt;No Put or Delete&lt;/td&gt;
&lt;td&gt;Does not show Put or Delete&lt;/td&gt;
&lt;td&gt;List and Get allowed; Put denied by RCP and Delete denied by SCP&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;RoleAdminWildcardNoBoundary&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;s3:*&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;No Put, Delete, or DeleteObjectVersion&lt;/td&gt;
&lt;td&gt;Retains those actions&lt;/td&gt;
&lt;td&gt;List and Get allowed; Put denied by RCP and Delete denied by SCP&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;The comparison shows an observable difference between the two findings. It does not determine the internal cause of the behavior by itself, but it does make it possible to compare what the analyzer reported with the actual API results.&lt;/p&gt;




&lt;h3&gt;
  
  
  View the findings in IAM Access Analyzer
&lt;/h3&gt;

&lt;p&gt;With the roles created and the analyzer active, the next step is to review what IAM Access Analyzer detected for the bucket.&lt;/p&gt;

&lt;p&gt;In the IAM console, under &lt;strong&gt;Access Analyzer&lt;/strong&gt;, you can see all active analyzers. In this lab, the relevant one is the &lt;strong&gt;internal access&lt;/strong&gt; analyzer.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fo8f24s9r642akbb99dcd.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fo8f24s9r642akbb99dcd.png" alt="IAM Access Analyzer analyzer list" width="799" height="457"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;When you open the analyzer and select the bucket, a list of findings appears. An important detail is that you will not see only the lab roles. You may also see internal roles from the bucket owner's account, such as administrative roles, AWS IAM Identity Center (SSO) roles, or service roles.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2F8n9xopq5vc3cymf41fh0.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2F8n9xopq5vc3cymf41fh0.png" alt="Internal access findings for the S3 bucket" width="800" height="534"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;If you expected to find only the four roles created for the lab, this is where you realize that another factor must be considered: &lt;strong&gt;the analyzer shows effective access to the resource&lt;/strong&gt;, not only the principals you manually added to the lab's bucket policy.&lt;/p&gt;

&lt;p&gt;This is one of the key points of the scenario: &lt;strong&gt;principals in the bucket owner's account do not need to be explicitly named in the bucket policy to have access&lt;/strong&gt;. If a role or user in the owning account already has permissions through its own IAM identity policy, it can still access the bucket. AWS documents this behavior for S3 here: &lt;a href="https://docs.aws.amazon.com/AmazonS3/latest/userguide/example-bucket-policies.html" rel="noopener noreferrer"&gt;Granting access to an IAM principal in the same account does not require updating the bucket policy&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;The bucket policy becomes necessary when you want to enable &lt;strong&gt;cross-account&lt;/strong&gt; access, as is the case with the lab roles.&lt;/p&gt;

&lt;h3&gt;
  
  
  What the findings list shows
&lt;/h3&gt;

&lt;p&gt;In the summary view, these are the most useful columns:&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Field&lt;/th&gt;
&lt;th&gt;Meaning&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Finding ID&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Unique identifier for the finding&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Resource&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;The analyzed bucket&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Resource owner account&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;The account that owns the bucket&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Principal&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;The role or principal that has access&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Condition&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Whether access depends on a condition&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Shared through&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;The layer that creates the access path, such as a bucket policy or bucket ACL&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Access level&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;General action categories: Read, List, Write, Permissions, and Tagging&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;RCP restriction&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Whether the analyzer considered a Resource Control Policy&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;SCP restriction&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Whether the analyzer considered a Service Control Policy&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Status&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Whether the finding is active or archived&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;Two columns deserve special attention in this lab:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;SCP restriction = Applied&lt;/strong&gt;&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;RCP restriction = Applied&lt;/strong&gt;&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;This does not necessarily mean that a specific action such as &lt;code&gt;s3:DeleteObject&lt;/code&gt; or &lt;code&gt;s3:PutObject&lt;/code&gt; disappeared from the list. It does mean that &lt;strong&gt;those layers were considered during the evaluation&lt;/strong&gt;.&lt;/p&gt;

&lt;h3&gt;
  
  
  View the details of a finding
&lt;/h3&gt;

&lt;p&gt;The summary list is useful, but the real value appears when you open the details of each finding.&lt;/p&gt;

&lt;p&gt;For &lt;code&gt;RoleReadOnly&lt;/code&gt;, the result is straightforward:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Principal:&lt;/strong&gt; &lt;code&gt;Account B / RoleReadOnly&lt;/code&gt;&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Principal account:&lt;/strong&gt; &lt;code&gt;Account B&lt;/code&gt;&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Shared through:&lt;/strong&gt; &lt;code&gt;Bucket policy&lt;/code&gt;&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Access reported by the finding:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;Read&lt;/code&gt; → &lt;code&gt;s3:GetObject&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;List&lt;/code&gt; → &lt;code&gt;s3:ListBucket&lt;/code&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The finding does not show only broad categories. It directly shows the actions the analyzer is reporting for that principal on the bucket.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fkfsev5w5jpn50g4wqgtc.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fkfsev5w5jpn50g4wqgtc.png" alt="RoleReadOnly finding summary" width="799" height="357"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fzirin4twk86zfrfn74ys.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fzirin4twk86zfrfn74ys.png" alt="RoleReadOnly finding actions" width="800" height="542"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  What this demonstrates about the SCP, RCP, and permissions boundary
&lt;/h3&gt;

&lt;p&gt;This is where one of the most interesting parts of the exercise appeared.&lt;/p&gt;

&lt;p&gt;At first, I only had &lt;code&gt;RoleReadOnly&lt;/code&gt; and &lt;code&gt;RoleAdmin&lt;/code&gt;, but I later added two roles to improve the comparison:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;RoleDiagnosticExplicitNoBoundary&lt;/code&gt;, with the actions declared individually.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;RoleAdminWildcardNoBoundary&lt;/code&gt;, with &lt;code&gt;s3:*&lt;/code&gt; and no permissions boundary.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;All four roles were subject to the same SCP and RCP, but their findings were not presented in exactly the same way.&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Role&lt;/th&gt;
&lt;th&gt;Configuration&lt;/th&gt;
&lt;th&gt;Observed finding&lt;/th&gt;
&lt;th&gt;Runtime result&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;RoleReadOnly&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;
&lt;code&gt;GetObject&lt;/code&gt; and &lt;code&gt;ListBucket&lt;/code&gt;
&lt;/td&gt;
&lt;td&gt;Shows &lt;code&gt;GetObject&lt;/code&gt; and &lt;code&gt;ListBucket&lt;/code&gt;
&lt;/td&gt;
&lt;td&gt;Read allowed; write and delete denied&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;RoleDiagnosticExplicitNoBoundary&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Get, Put, Delete, List, and GetBucketPolicy declared explicitly&lt;/td&gt;
&lt;td&gt;Does not show &lt;code&gt;PutObject&lt;/code&gt; or &lt;code&gt;DeleteObject&lt;/code&gt;
&lt;/td&gt;
&lt;td&gt;List and Get allowed; Put denied by RCP and Delete denied by SCP&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;RoleAdmin&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;
&lt;code&gt;s3:*&lt;/code&gt; limited by a permissions boundary&lt;/td&gt;
&lt;td&gt;Shows only &lt;code&gt;GetObject&lt;/code&gt; and &lt;code&gt;ListBucket&lt;/code&gt;
&lt;/td&gt;
&lt;td&gt;Put denied by RCP, Delete by SCP, and GetBucketPolicy by boundary&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;RoleAdminWildcardNoBoundary&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;
&lt;code&gt;s3:*&lt;/code&gt; without a permissions boundary&lt;/td&gt;
&lt;td&gt;Retains &lt;code&gt;PutObject&lt;/code&gt;, &lt;code&gt;DeleteObject&lt;/code&gt;, and &lt;code&gt;DeleteObjectVersion&lt;/code&gt;
&lt;/td&gt;
&lt;td&gt;List and Get allowed; Put denied by RCP and Delete denied by SCP&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;Using the four main lab operations as the reference point—&lt;code&gt;ListBucket&lt;/code&gt;, &lt;code&gt;GetObject&lt;/code&gt;, &lt;code&gt;PutObject&lt;/code&gt;, and &lt;code&gt;DeleteObject&lt;/code&gt;—the findings for the first three cases matched the action set I expected after considering the different policy layers.&lt;/p&gt;

&lt;p&gt;The difference appeared with &lt;code&gt;RoleAdminWildcardNoBoundary&lt;/code&gt;. Its finding continued to include actions that the runtime tests confirmed were denied:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;PutObject&lt;/code&gt; returned &lt;code&gt;AccessDenied&lt;/code&gt; because of an explicit deny in the RCP.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;DeleteObject&lt;/code&gt; returned &lt;code&gt;AccessDenied&lt;/code&gt; because of an explicit deny in the SCP.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;At the same time, both restrictions appeared in the findings as:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;resourceControlPolicyRestriction: APPLIED
serviceControlPolicyRestriction: APPLIED
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;For the complete test set, I waited at least 24 hours after making the changes before reviewing the findings again. I also used &lt;code&gt;get-finding-v2&lt;/code&gt; to confirm that the results had been reanalyzed.&lt;/p&gt;

&lt;p&gt;This prevented me from comparing findings that might still have been waiting for an update.&lt;/p&gt;

&lt;p&gt;This does not mean that the SCP or the RCP failed. The runtime tests show exactly the opposite: both policies blocked the operations.&lt;/p&gt;

&lt;p&gt;What I can document is a difference between the actions reported by the finding and the actions the role could actually perform in this specific scenario.&lt;/p&gt;

&lt;p&gt;My final interpretation of the lab is therefore:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;&lt;strong&gt;The finding is very useful for identifying principals, access paths, and reported actions on the resource.&lt;/strong&gt;&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;The SCP and RCP columns indicate whether those layers participated in the evaluation.&lt;/strong&gt;&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;When you need to validate a specific API, a runtime test remains important evidence.&lt;/strong&gt;&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;If the finding and runtime results do not match, review IAM, the bucket policy, the boundary, the SCP, and the RCP directly, and confirm that the analyzer had enough time to update before drawing a conclusion.&lt;/strong&gt;&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;I am not attempting to determine the internal cause of this difference from the outside or to generalize the result to every service or configuration. The goal is to document the configuration and evidence in a reproducible way.&lt;/p&gt;

&lt;p&gt;The official reference used to interpret the &lt;code&gt;APPLIED&lt;/code&gt; fields is: &lt;a href="https://docs.aws.amazon.com/access-analyzer/latest/APIReference/API_InternalAccessDetails.html" rel="noopener noreferrer"&gt;InternalAccessDetails – AWS IAM Access Analyzer API&lt;/a&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  Why other roles appear in addition to the lab roles
&lt;/h3&gt;

&lt;p&gt;The findings may also include internal roles such as federated administrators, Control Tower roles, and service roles from the account that owns the bucket.&lt;/p&gt;

&lt;p&gt;This &lt;strong&gt;does not mean&lt;/strong&gt; that the lab's bucket policy granted access to all of them.&lt;/p&gt;

&lt;p&gt;It means that the bucket is also accessible to &lt;strong&gt;internal principals in the resource owner's account&lt;/strong&gt;, and Access Analyzer displays them because its job is not to show only the cross-account access created for the exercise. It shows the &lt;strong&gt;total effective access within the analyzer's scope&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;Put another way:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Account A&lt;/strong&gt; owns the bucket.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Account B&lt;/strong&gt; contains the four roles used in the lab.&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The lab roles explain the &lt;strong&gt;cross-account&lt;/strong&gt; scenario.&lt;br&gt;&lt;br&gt;
The other findings show &lt;strong&gt;intra-account&lt;/strong&gt; access from roles that already exist in &lt;strong&gt;Account A&lt;/strong&gt; and have permissions through other layers in the environment.&lt;/p&gt;

&lt;p&gt;This is already easy to lose track of in a small lab. Now imagine the same problem in a real organization with tens or hundreds of accounts, thousands of roles, federated SSO roles, service roles, Control Tower roles, managed policies, inline policies, bucket policies, ACLs, SCPs, RCPs, permissions boundaries, and many resources beyond a single bucket. At that point, answering “who can actually access what?” stops being a manual policy review and becomes a problem of correlating multiple permission layers.&lt;/p&gt;

&lt;p&gt;That is the core difficulty: effective access does not live in a single policy. It comes from the combination of identity policies, resource policies, intra-account permissions, cross-account permissions, and organizational restrictions. If you review only a bucket policy, you may think you understand access to the resource, while actually missing internal principals in the same account, inherited roles, service roles, or access paths that remain valid through other mechanisms. The more accounts, roles, and resources you add, the harder it becomes to distinguish between expected access, inherited access, indirect access, and access that represents real risk.&lt;/p&gt;

&lt;p&gt;That is why tools such as IAM Access Analyzer become valuable at scale: not because they replace understanding IAM, but because they help reduce operational complexity when you are no longer looking at four lab roles, but at hundreds or thousands of identities with cross-account permissions over many resources.&lt;/p&gt;




&lt;h4&gt;
  
  
  What the Archive button does
&lt;/h4&gt;

&lt;p&gt;Archiving a finding does not modify access or change any policies. It only changes how that result is managed inside the analyzer.&lt;/p&gt;

&lt;p&gt;When you archive a finding:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;It no longer appears among active findings.&lt;/li&gt;
&lt;li&gt;It remains available as an archived finding.&lt;/li&gt;
&lt;li&gt;You can use archive rules to automatically archive new findings that match defined criteria.&lt;/li&gt;
&lt;li&gt;When creating a rule, you can choose to apply it only to new findings or also archive existing active findings.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;If the access path disappears, the finding moves to &lt;code&gt;Resolved&lt;/code&gt;. If the path changes significantly, Access Analyzer may resolve the previous finding and generate a new one.&lt;/p&gt;




&lt;h3&gt;
  
  
  Lab results
&lt;/h3&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Role&lt;/th&gt;
&lt;th&gt;ListBucket&lt;/th&gt;
&lt;th&gt;GetObject&lt;/th&gt;
&lt;th&gt;PutObject&lt;/th&gt;
&lt;th&gt;DeleteObject&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;RoleReadOnly&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;✅ Allowed&lt;/td&gt;
&lt;td&gt;✅ Allowed&lt;/td&gt;
&lt;td&gt;❌ RCP&lt;/td&gt;
&lt;td&gt;❌ IAM&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;RoleAdmin&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;✅ Allowed&lt;/td&gt;
&lt;td&gt;✅ Allowed&lt;/td&gt;
&lt;td&gt;❌ RCP&lt;/td&gt;
&lt;td&gt;❌ SCP&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;RoleDiagnosticExplicitNoBoundary&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;✅ Allowed&lt;/td&gt;
&lt;td&gt;✅ Allowed&lt;/td&gt;
&lt;td&gt;❌ RCP&lt;/td&gt;
&lt;td&gt;❌ SCP&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;RoleAdminWildcardNoBoundary&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;✅ Allowed&lt;/td&gt;
&lt;td&gt;✅ Allowed&lt;/td&gt;
&lt;td&gt;❌ RCP&lt;/td&gt;
&lt;td&gt;❌ SCP&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;The table includes only API calls that were actually executed. The findings comparison is covered in the previous section.&lt;/p&gt;




&lt;h3&gt;
  
  
  What this lab demonstrates
&lt;/h3&gt;

&lt;p&gt;For a cross-account action to work in this scenario, the following conditions must be met:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;An identity policy on the role in Account B must allow the action.&lt;/li&gt;
&lt;li&gt;A bucket policy in Account A must grant that action to the external role.&lt;/li&gt;
&lt;li&gt;No explicit deny applicable to that action and resource can exist.&lt;/li&gt;
&lt;li&gt;If the role has a permissions boundary, the action must be included within its maximum allowed permissions.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;This does not mean that SCPs or RCPs cannot exist. Both exist in this lab, but they block specific operations: the SCP blocks &lt;code&gt;DeleteObject&lt;/code&gt; and &lt;code&gt;DeleteObjectVersion&lt;/code&gt;, while the RCP blocks &lt;code&gt;PutObject&lt;/code&gt;. The remaining actions continue to be available according to the other applicable policies.&lt;/p&gt;

&lt;p&gt;The first part of the lab demonstrates this with the SCP: the role has IAM permission, the bucket policy also grants it, but the organizational explicit deny wins.&lt;/p&gt;

&lt;p&gt;The RCP extension reinforces the same idea from the resource side: even when an access path exists, an organizational control can still block the operation at runtime.&lt;/p&gt;

&lt;p&gt;The &lt;code&gt;GetBucketPolicy&lt;/code&gt; test adds a third important lesson: a &lt;strong&gt;permissions boundary&lt;/strong&gt; can also act as a limiting layer and can be explicitly identified in an &lt;code&gt;AccessDenied&lt;/code&gt; message.&lt;/p&gt;

&lt;p&gt;The lab therefore demonstrates more than the deny itself. It also shows something more useful in production: &lt;strong&gt;you need to understand how to read the finding and interpret runtime errors without assuming that one is a perfect mirror of the other&lt;/strong&gt;.&lt;/p&gt;




&lt;h2&gt;
  
  
  Before enabling it in production: pricing
&lt;/h2&gt;

&lt;p&gt;This is something that very few tutorials mention, and it is worth understanding before enabling the feature in production.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;IAM Access Analyzer Internal Access is not free&lt;/strong&gt; for effective-permissions analysis.&lt;/p&gt;

&lt;p&gt;Some practical considerations:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Do not enable it for every resource by default.&lt;/strong&gt; Start with buckets that contain sensitive or regulated data.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Use tags to identify critical resources&lt;/strong&gt;—for example, &lt;code&gt;DataClassification: Confidential&lt;/code&gt;—and limit the analyzer's scope to those resources.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;It makes the most sense for specific high-risk resources&lt;/strong&gt;: buckets containing PCI data, security log repositories, cross-account shared buckets, DynamoDB tables, RDS database snapshots, and RDS cluster snapshots.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;For the remaining resources, IAM Access Advisor can help prioritize periodic manual reviews and reduce the number of resources for which Internal Access Analyzer needs to be enabled.&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The concrete recommendation is to first define what qualifies as a critical resource in your organization, apply data-classification tags, and use Access Analyzer Internal Access only for that subset—not for everything.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Practical note:&lt;/strong&gt; if you were thinking of enabling it briefly and then deleting it, be careful. In my case, the charge appeared on the same day I enabled it and reflected the full monthly unit for the monitored resource. That alone is a reason to plan the experiment carefully before enabling it.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fqqxvbsd25l2tebnovd7w.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fqqxvbsd25l2tebnovd7w.png" alt="Internal Access Analyzer charge" width="800" height="382"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;I would add another recommendation based on what happened in this lab: &lt;strong&gt;before enabling it for critical resources, understand exactly which questions you want it to answer&lt;/strong&gt;.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;If your question is &lt;strong&gt;“who can reach this resource, and through which access path?”&lt;/strong&gt;, the analyzer provides substantial value.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;If your question is &lt;strong&gt;“was this specific API effectively blocked by this organizational or identity control layer?”&lt;/strong&gt;, you will probably also want a runtime validation to avoid surprises when interpreting the finding.&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;




&lt;h2&gt;
  
  
  Red Team perspective
&lt;/h2&gt;

&lt;p&gt;Access Analyzer is not useful only for defensive teams, but this section requires some context.&lt;/p&gt;

&lt;p&gt;A user or role needs permissions to query the analyzer and its findings. In addition, calls made through the console or the IAM Access Analyzer APIs are recorded in AWS CloudTrail.&lt;/p&gt;

&lt;p&gt;It is therefore incorrect to assume that any compromised role in a member account can query the organizational analyzer, or that using it would be invisible.&lt;/p&gt;

&lt;p&gt;In an authorized internal Red Team engagement, an identity that already has access to the analyzer could use the findings to:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Review access paths without directly calling the target resource.&lt;/li&gt;
&lt;li&gt;Prioritize relevant cross-account relationships.&lt;/li&gt;
&lt;li&gt;Compare reported access with executable access.&lt;/li&gt;
&lt;li&gt;Evaluate the impact of SCPs, RCPs, and permissions boundaries.&lt;/li&gt;
&lt;/ol&gt;




&lt;h2&gt;
  
  
  What Access Analyzer shows—and what it does not
&lt;/h2&gt;

&lt;p&gt;To make it clear:&lt;/p&gt;

&lt;p&gt;✅ Which identities have access to a specific resource&lt;br&gt;&lt;br&gt;
✅ Which layers participated in the evaluation, including IAM, resource policies, SCPs, and RCPs&lt;br&gt;&lt;br&gt;
✅ Exportable findings for audit evidence&lt;br&gt;&lt;br&gt;
✅ Centralized organization-wide visibility from the account that manages the analyzer&lt;/p&gt;

&lt;p&gt;The lab also made the following points much clearer to me:&lt;/p&gt;

&lt;p&gt;⚠️ I would not always use it as the only evidence that a specific API has disappeared from effective access simply because an SCP or RCP blocks it at runtime&lt;br&gt;&lt;br&gt;
⚠️ The &lt;code&gt;AccessDenied&lt;/code&gt; message may not always show every layer that could be contributing to the denial&lt;br&gt;&lt;br&gt;
⚠️ For specific denials caused by an organizational control or a permissions boundary, it is useful to complement the analysis with a controlled runtime test&lt;/p&gt;




&lt;h2&gt;
  
  
  Conclusion
&lt;/h2&gt;

&lt;p&gt;The question that started this series was:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Who can actually access your AWS resources?&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;After this lab, the practical answer remains that you cannot determine it by reviewing a single policy.&lt;/p&gt;

&lt;p&gt;IAM Access Analyzer Internal Access provides value by identifying principals, actions, and access paths for critical resources. It also centralizes results at the organization level and makes the findings available through an API.&lt;/p&gt;

&lt;p&gt;The lab also produced a specific observation. Using the four main operations as the reference point, the finding matched the expected action set in three controlled cases. For the role with &lt;code&gt;s3:*&lt;/code&gt; and no permissions boundary, the finding continued to show &lt;code&gt;PutObject&lt;/code&gt;, &lt;code&gt;DeleteObject&lt;/code&gt;, and &lt;code&gt;DeleteObjectVersion&lt;/code&gt;, even though the restrictions appeared as &lt;code&gt;APPLIED&lt;/code&gt;. Runtime tests confirmed that the RCP denied &lt;code&gt;PutObject&lt;/code&gt; and the SCP denied &lt;code&gt;DeleteObject&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;I will not speculate about the exact cause. What I can do is document the configuration, findings, and runtime tests so that the result can be reproduced.&lt;/p&gt;

&lt;p&gt;Because Internal Access Analyzer is a paid capability, I also recommend thinking carefully about the questions you want to answer before enabling it. If you need to identify who can access a critical bucket and through which path, it can be especially useful. But if you only need to validate a specific API, a policy review and a controlled runtime test may be enough.&lt;/p&gt;

&lt;p&gt;The cost is calculated per monitored resource, per analyzer, and per Region. Enabling it without first defining the scope can therefore result in a larger-than-expected charge. In production, I would start with genuinely critical resources, such as buckets containing sensitive data, security logs, or resources shared across accounts, and expand the scope only when there is a clear need.&lt;/p&gt;

&lt;p&gt;For me, the most useful interpretation of the exercise is:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Use the analyzer to gain visibility.&lt;/li&gt;
&lt;li&gt;Understand what each field means.&lt;/li&gt;
&lt;li&gt;Validate critical APIs when you need a specific answer.&lt;/li&gt;
&lt;li&gt;Define the scope before enabling it.&lt;/li&gt;
&lt;li&gt;Avoid interpreting a single screen without reviewing the other permission layers.&lt;/li&gt;
&lt;/ul&gt;




&lt;p&gt;💡 &lt;strong&gt;Have you found unexpected access while analyzing your resources?&lt;/strong&gt; That is often the moment when tools like this go from “interesting” to “necessary.”&lt;/p&gt;

</description>
      <category>aws</category>
      <category>security</category>
      <category>iam</category>
    </item>
    <item>
      <title>🛡️ AWS IAM Access Analyzer — Parte 3/4</title>
      <dc:creator>Terry Quispe Paniagua</dc:creator>
      <pubDate>Sun, 26 Jul 2026 16:16:17 +0000</pubDate>
      <link>https://dev.to/terry_cloud/aws-iam-access-analyzer-parte-34-46o9</link>
      <guid>https://dev.to/terry_cloud/aws-iam-access-analyzer-parte-34-46o9</guid>
      <description>&lt;h2&gt;
  
  
  Demo real: permisos efectivos, SCPs, RCPs y una comparación que no salió como esperaba
&lt;/h2&gt;




&lt;p&gt;En la parte anterior expliqué el problema: en entornos con múltiples cuentas, roles y políticas en capas, saber quién tiene acceso real a un recurso no es algo que puedas responder abriendo la consola y revisando una IAM policy. Si todavía no leíste la primera parte, te recomiendo &lt;a href="https://dev.to/terry_cloud/-aws-iam-access-analyzer-parte-24-2lab"&gt;empezar por ahí&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;En este post voy directo al laboratorio. Configuré un escenario con dos cuentas dentro de la misma AWS Organization, un bucket S3 en una cuenta, varios roles IAM en otra y distintas capas de control sobre el acceso: &lt;strong&gt;IAM&lt;/strong&gt;, &lt;strong&gt;bucket policy&lt;/strong&gt;, &lt;strong&gt;SCP&lt;/strong&gt;, &lt;strong&gt;RCP&lt;/strong&gt; y un &lt;strong&gt;permissions boundary&lt;/strong&gt; sobre el rol administrador.&lt;/p&gt;

&lt;p&gt;El objetivo no fue solo comprobar si una acción funcionaba o no, sino comparar qué estaba configurado, qué reportaba &lt;strong&gt;IAM Access Analyzer Internal Access&lt;/strong&gt; y qué ocurría realmente al ejecutar las APIs.&lt;/p&gt;




&lt;blockquote&gt;
&lt;p&gt;⚠️ &lt;strong&gt;Nota sobre costos antes de empezar&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;IAM Access Analyzer External Access &lt;strong&gt;no tiene costo adicional&lt;/strong&gt;, pero Internal Access Analyzer &lt;strong&gt;sí es una capacidad pagada&lt;/strong&gt;. AWS cobra &lt;strong&gt;$9.00 USD por recurso monitoreado, por analizador y por región, al mes&lt;/strong&gt;. En este lab solo se monitorea un bucket S3, así que el costo estimado fue &lt;strong&gt;$9.00 USD/mes&lt;/strong&gt; mientras el analizador estuvo activo. Si lo habilitas sobre múltiples recursos en producción, el costo escala linealmente: 10 recursos = $90 USD/mes, 38 recursos en 5 cuentas = $342 USD/mes (ejemplo publicado en la &lt;a href="https://aws.amazon.com/es/iam/access-analyzer/pricing/" rel="noopener noreferrer"&gt;página oficial de precios de AWS&lt;/a&gt;).&lt;/p&gt;
&lt;/blockquote&gt;




&lt;h2&gt;
  
  
  Lab: validando acceso &lt;strong&gt;cross-account dentro de la misma AWS Organization&lt;/strong&gt; con IAM Access Analyzer Internal Access
&lt;/h2&gt;

&lt;p&gt;En este laboratorio validé cómo IAM Access Analyzer Internal Access representa el acceso de roles ubicados en una cuenta hacia un bucket S3 ubicado en otra cuenta de la misma organización.&lt;/p&gt;

&lt;p&gt;El escenario empezó con dos roles:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;&lt;code&gt;RoleReadOnly&lt;/code&gt;&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;code&gt;RoleAdmin&lt;/code&gt;&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Después añadí dos roles de control para comparar si la forma de declarar las acciones influía en el resultado:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;&lt;code&gt;RoleDiagnosticExplicitNoBoundary&lt;/code&gt;&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;code&gt;RoleAdminWildcardNoBoundary&lt;/code&gt;&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;También añadí tres controles:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;SCP&lt;/strong&gt; para bloquear &lt;code&gt;s3:DeleteObject&lt;/code&gt; y &lt;code&gt;s3:DeleteObjectVersion&lt;/code&gt;&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;RCP&lt;/strong&gt; para bloquear &lt;code&gt;s3:PutObject&lt;/code&gt;&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Permissions boundary&lt;/strong&gt; sobre &lt;code&gt;RoleAdmin&lt;/code&gt;&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Arquitectura
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;AWS Organization
└── OU del laboratorio
    ├── Cuenta A — propietaria del recurso
    │   └── Bucket S3: demo-internal-aa-2026
    │
    └── Cuenta B — propietaria de los principals
        ├── RoleReadOnly
        ├── RoleAdmin
        ├── RoleDiagnosticExplicitNoBoundary
        └── RoleAdminWildcardNoBoundary
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;La Cuenta A contiene el bucket S3 y la Cuenta B contiene los roles que intentarán acceder a él. Ambas cuentas están dentro de la misma OU, y tanto la SCP como la RCP se adjuntaron a esa OU. La SCP limita las acciones disponibles para los principals de las cuentas que contiene, mientras que la RCP limita el acceso sobre sus recursos; en este lab, el ARN definido en la RCP restringe el deny al bucket de la Cuenta A. El permissions boundary se adjunta directamente a &lt;code&gt;RoleAdmin&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fqrv2efookam767lmxtca.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fqrv2efookam767lmxtca.png" alt=" " width="799" height="517"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;La imagen anterior corresponde a la primera fase del laboratorio, cuando todavía trabajaba únicamente con &lt;code&gt;RoleReadOnly&lt;/code&gt; y &lt;code&gt;RoleAdmin&lt;/code&gt;. Los otros dos roles se añadieron después para controlar mejor la comparación.&lt;/p&gt;

&lt;h3&gt;
  
  
  Objetivo
&lt;/h3&gt;

&lt;p&gt;El laboratorio comprueba siete cosas:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;&lt;p&gt;Qué roles dentro de la organización aparecen con acceso al bucket.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Qué acciones reporta el finding para cada rol.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Cómo una SCP bloquea una acción aunque IAM y la bucket policy la permitan.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Cómo una RCP bloquea una acción desde el lado del recurso.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Cómo un permissions boundary limita el máximo permiso de un rol.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Qué cambia al declarar acciones explícitas frente a utilizar &lt;code&gt;s3:*&lt;/code&gt;.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Si el resultado del finding coincide con la ejecución real de las APIs.&lt;/p&gt;&lt;/li&gt;
&lt;/ol&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Rol&lt;/th&gt;
&lt;th&gt;Permiso configurado&lt;/th&gt;
&lt;th&gt;Boundary&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;RoleReadOnly&lt;/td&gt;
&lt;td&gt;ListBucket y GetObject&lt;/td&gt;
&lt;td&gt;No&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;RoleAdmin&lt;/td&gt;
&lt;td&gt;s3:*&lt;/td&gt;
&lt;td&gt;Sí: limita a ListBucket, GetObject, PutObject y DeleteObject&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;RoleAdminWildcardNoBoundary&lt;/td&gt;
&lt;td&gt;s3:*&lt;/td&gt;
&lt;td&gt;No&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;RoleDiagnosticExplicitNoBoundary&lt;/td&gt;
&lt;td&gt;ListBucket, GetBucketPolicy, GetObject, PutObject y DeleteObject&lt;/td&gt;
&lt;td&gt;No&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;La &lt;strong&gt;SCP&lt;/strong&gt; bloquea &lt;code&gt;s3:DeleteObject&lt;/code&gt; y &lt;code&gt;s3:DeleteObjectVersion&lt;/code&gt;.&lt;br&gt;&lt;br&gt;
La &lt;strong&gt;RCP&lt;/strong&gt; bloquea &lt;code&gt;s3:PutObject&lt;/code&gt; sobre el bucket del lab. &lt;/p&gt;


&lt;h3&gt;
  
  
  Paso 1: Crear los roles en la Cuenta B
&lt;/h3&gt;

&lt;p&gt;Desde CloudShell en la Cuenta B, se definen las variables de entorno:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;export &lt;/span&gt;&lt;span class="nv"&gt;ACCOUNT_B_ID&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"&amp;lt;ACCOUNT_B_ID&amp;gt;"&lt;/span&gt;
&lt;span class="nb"&gt;export &lt;/span&gt;&lt;span class="nv"&gt;BUCKET_NAME&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"demo-internal-aa-2026"&lt;/span&gt; &lt;span class="c"&gt;# ejemplo&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Se valida la identidad actual:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;aws sts get-caller-identity
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Luego se crea la trust policy para permitir asumir los roles desde la misma Cuenta B:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;cat&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;&lt;/span&gt; trust-account-b.json &lt;span class="o"&gt;&amp;lt;&amp;lt;&lt;/span&gt;&lt;span class="no"&gt;EOF&lt;/span&gt;&lt;span class="sh"&gt;
{
  "Version": "2012-10-17",
  "Statement": [
    {
      "Sid": "AllowAssumeRoleFromAccountB",
      "Effect": "Allow",
      "Principal": {
        "AWS": "arn:aws:iam::&lt;/span&gt;&lt;span class="nv"&gt;$ACCOUNT_B_ID&lt;/span&gt;&lt;span class="sh"&gt;:root"
      },
      "Action": "sts:AssumeRole"
    }
  ]
}
&lt;/span&gt;&lt;span class="no"&gt;EOF
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Se crean los cuatro roles:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="k"&gt;for &lt;/span&gt;ROLE &lt;span class="k"&gt;in&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
  RoleReadOnly &lt;span class="se"&gt;\&lt;/span&gt;
  RoleAdmin &lt;span class="se"&gt;\&lt;/span&gt;
  RoleDiagnosticExplicitNoBoundary &lt;span class="se"&gt;\&lt;/span&gt;
  RoleAdminWildcardNoBoundary
&lt;span class="k"&gt;do
  &lt;/span&gt;aws iam create-role &lt;span class="se"&gt;\&lt;/span&gt;
    &lt;span class="nt"&gt;--role-name&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$ROLE&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
    &lt;span class="nt"&gt;--assume-role-policy-document&lt;/span&gt; file://trust-account-b.json
&lt;span class="k"&gt;done&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;h3&gt;
  
  
  Paso 2: Asignar permisos IAM a los roles
&lt;/h3&gt;

&lt;p&gt;&lt;strong&gt;RoleReadOnly&lt;/strong&gt; — solo puede listar y leer:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;cat&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;&lt;/span&gt; role-readonly-policy.json &lt;span class="o"&gt;&amp;lt;&amp;lt;&lt;/span&gt;&lt;span class="no"&gt;EOF&lt;/span&gt;&lt;span class="sh"&gt;
{
  "Version": "2012-10-17",
  "Statement": [
    {
      "Sid": "ListBucket",
      "Effect": "Allow",
      "Action": "s3:ListBucket",
      "Resource": "arn:aws:s3:::&lt;/span&gt;&lt;span class="nv"&gt;$BUCKET_NAME&lt;/span&gt;&lt;span class="sh"&gt;"
    },
    {
      "Sid": "ReadObjects",
      "Effect": "Allow",
      "Action": "s3:GetObject",
      "Resource": "arn:aws:s3:::&lt;/span&gt;&lt;span class="nv"&gt;$BUCKET_NAME&lt;/span&gt;&lt;span class="sh"&gt;/*"
    }
  ]
}
&lt;/span&gt;&lt;span class="no"&gt;EOF

&lt;/span&gt;aws iam put-role-policy &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--role-name&lt;/span&gt; RoleReadOnly &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--policy-name&lt;/span&gt; DemoBucketReadOnlyAccess &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--policy-document&lt;/span&gt; file://role-readonly-policy.json
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;RoleAdmin&lt;/strong&gt; — Permite &lt;code&gt;s3:*&lt;/code&gt; sobre el bucket y sus objetos:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;cat&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;&lt;/span&gt; role-admin-policy.json &lt;span class="o"&gt;&amp;lt;&amp;lt;&lt;/span&gt;&lt;span class="no"&gt;EOF&lt;/span&gt;&lt;span class="sh"&gt;
{
  "Version": "2012-10-17",
  "Statement": [
    {
      "Sid": "AdminBucketAccess",
      "Effect": "Allow",
      "Action": "s3:*",
      "Resource": [
        "arn:aws:s3:::&lt;/span&gt;&lt;span class="nv"&gt;$BUCKET_NAME&lt;/span&gt;&lt;span class="sh"&gt;",
        "arn:aws:s3:::&lt;/span&gt;&lt;span class="nv"&gt;$BUCKET_NAME&lt;/span&gt;&lt;span class="sh"&gt;/*"
      ]
    }
  ]
}
&lt;/span&gt;&lt;span class="no"&gt;EOF

&lt;/span&gt;aws iam put-role-policy &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--role-name&lt;/span&gt; RoleAdmin &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--policy-name&lt;/span&gt; DemoBucketAdminAccess &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--policy-document&lt;/span&gt; file://role-admin-policy.json
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;RoleDiagnosticExplicitNoBoundary&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Este rol declara de manera explícita las acciones que me interesaba comparar:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;cat&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;&lt;/span&gt; role-diagnostic-explicit-policy.json &lt;span class="o"&gt;&amp;lt;&amp;lt;&lt;/span&gt;&lt;span class="no"&gt;EOF&lt;/span&gt;&lt;span class="sh"&gt;
{
  "Version": "2012-10-17",
  "Statement": [
    {
      "Sid": "DiagnosticBucketActions",
      "Effect": "Allow",
      "Action": [
        "s3:ListBucket",
        "s3:GetBucketPolicy"
      ],
      "Resource": "arn:aws:s3:::&lt;/span&gt;&lt;span class="nv"&gt;$BUCKET_NAME&lt;/span&gt;&lt;span class="sh"&gt;"
    },
    {
      "Sid": "DiagnosticObjectActions",
      "Effect": "Allow",
      "Action": [
        "s3:GetObject",
        "s3:PutObject",
        "s3:DeleteObject"
      ],
      "Resource": "arn:aws:s3:::&lt;/span&gt;&lt;span class="nv"&gt;$BUCKET_NAME&lt;/span&gt;&lt;span class="sh"&gt;/*"
    }
  ]
}
&lt;/span&gt;&lt;span class="no"&gt;EOF

&lt;/span&gt;aws iam put-role-policy &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--role-name&lt;/span&gt; RoleDiagnosticExplicitNoBoundary &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--policy-name&lt;/span&gt; DiagnosticExplicitNoBoundaryAccess &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--policy-document&lt;/span&gt; file://role-diagnostic-explicit-policy.json
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;RoleAdminWildcardNoBoundary&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Este rol mantiene el wildcard y no tiene boundary:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;cat&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;&lt;/span&gt; role-admin-wildcard-policy.json &lt;span class="o"&gt;&amp;lt;&amp;lt;&lt;/span&gt;&lt;span class="no"&gt;EOF&lt;/span&gt;&lt;span class="sh"&gt;
{
  "Version": "2012-10-17",
  "Statement": [
    {
      "Sid": "AdminWildcardBucketAccess",
      "Effect": "Allow",
      "Action": "s3:*",
      "Resource": [
        "arn:aws:s3:::&lt;/span&gt;&lt;span class="nv"&gt;$BUCKET_NAME&lt;/span&gt;&lt;span class="sh"&gt;",
        "arn:aws:s3:::&lt;/span&gt;&lt;span class="nv"&gt;$BUCKET_NAME&lt;/span&gt;&lt;span class="sh"&gt;/*"
      ]
    }
  ]
}
&lt;/span&gt;&lt;span class="no"&gt;EOF

&lt;/span&gt;aws iam put-role-policy &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--role-name&lt;/span&gt; RoleAdminWildcardNoBoundary &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--policy-name&lt;/span&gt; AdminWildcardNoBoundaryAccess &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--policy-document&lt;/span&gt; file://role-admin-wildcard-policy.json
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;h3&gt;
  
  
  Paso 2.1: Añadir permissions boundary a RoleAdmin
&lt;/h3&gt;

&lt;p&gt;Además de su inline policy con &lt;code&gt;s3:*&lt;/code&gt;, &lt;code&gt;RoleAdmin&lt;/code&gt; tiene un permissions boundary.&lt;/p&gt;

&lt;p&gt;La idea no fue reemplazar la SCP ni la RCP. Quería añadir una capa que materializara un conjunto explícito de acciones máximas para ese rol.&lt;/p&gt;

&lt;p&gt;El boundary permite:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;&lt;code&gt;s3:ListBucket&lt;/code&gt;&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;code&gt;s3:GetObject&lt;/code&gt;&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;code&gt;s3:PutObject&lt;/code&gt;&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;code&gt;s3:DeleteObject&lt;/code&gt;&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;No permite, por ejemplo:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;&lt;code&gt;s3:GetBucketPolicy&lt;/code&gt;&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;code&gt;s3:GetBucketAcl&lt;/code&gt;&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;code&gt;s3:GetBucketVersioning&lt;/code&gt;&lt;br&gt;
&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;cat&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;&lt;/span&gt; role-admin-boundary.json &lt;span class="o"&gt;&amp;lt;&amp;lt;&lt;/span&gt;&lt;span class="no"&gt;EOF&lt;/span&gt;&lt;span class="sh"&gt;
{
  "Version": "2012-10-17",
  "Statement": [
    {
      "Sid": "ListBucket",
      "Effect": "Allow",
      "Action": "s3:ListBucket",
      "Resource": "arn:aws:s3:::&lt;/span&gt;&lt;span class="nv"&gt;$BUCKET_NAME&lt;/span&gt;&lt;span class="sh"&gt;"
    },
    {
      "Sid": "PowerUserObjects",
      "Effect": "Allow",
      "Action": [
        "s3:GetObject",
        "s3:PutObject",
        "s3:DeleteObject"
      ],
      "Resource": "arn:aws:s3:::&lt;/span&gt;&lt;span class="nv"&gt;$BUCKET_NAME&lt;/span&gt;&lt;span class="sh"&gt;/*"
    }
  ]
}
&lt;/span&gt;&lt;span class="no"&gt;EOF

&lt;/span&gt;aws iam create-policy &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--policy-name&lt;/span&gt; boundary-adminuser &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--policy-document&lt;/span&gt; file://role-admin-boundary.json

aws iam put-role-permissions-boundary &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--role-name&lt;/span&gt; RoleAdmin &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--permissions-boundary&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
    &lt;span class="s2"&gt;"arn:aws:iam::&lt;/span&gt;&lt;span class="nv"&gt;$ACCOUNT_B_ID&lt;/span&gt;&lt;span class="s2"&gt;:policy/boundary-adminuser"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Validación:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;aws iam get-role &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--role-name&lt;/span&gt; RoleAdmin &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--query&lt;/span&gt; &lt;span class="s1"&gt;'Role.PermissionsBoundary'&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--output&lt;/span&gt; json
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;h3&gt;
  
  
  Paso 3: Configurar la bucket policy en la Cuenta A
&lt;/h3&gt;

&lt;p&gt;Desde CloudShell en la Cuenta A:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;export &lt;/span&gt;&lt;span class="nv"&gt;ACCOUNT_B_ID&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"&amp;lt;ACCOUNT_B_ID&amp;gt;"&lt;/span&gt;
&lt;span class="nb"&gt;export &lt;/span&gt;&lt;span class="nv"&gt;BUCKET_NAME&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"demo-internal-aa-2026"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;La bucket policy concede a cada rol las mismas acciones utilizadas en su identity policy:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;cat&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;&lt;/span&gt; bucket-policy.json &lt;span class="o"&gt;&amp;lt;&amp;lt;&lt;/span&gt;&lt;span class="no"&gt;EOF&lt;/span&gt;&lt;span class="sh"&gt;
{
  "Version": "2012-10-17",
  "Statement": [
    {
      "Sid": "AllowReadOnlyListBucket",
      "Effect": "Allow",
      "Principal": {
        "AWS": "arn:aws:iam::&lt;/span&gt;&lt;span class="nv"&gt;$ACCOUNT_B_ID&lt;/span&gt;&lt;span class="sh"&gt;:role/RoleReadOnly"
      },
      "Action": "s3:ListBucket",
      "Resource": "arn:aws:s3:::&lt;/span&gt;&lt;span class="nv"&gt;$BUCKET_NAME&lt;/span&gt;&lt;span class="sh"&gt;"
    },
    {
      "Sid": "AllowReadOnlyGetObject",
      "Effect": "Allow",
      "Principal": {
        "AWS": "arn:aws:iam::&lt;/span&gt;&lt;span class="nv"&gt;$ACCOUNT_B_ID&lt;/span&gt;&lt;span class="sh"&gt;:role/RoleReadOnly"
      },
      "Action": "s3:GetObject",
      "Resource": "arn:aws:s3:::&lt;/span&gt;&lt;span class="nv"&gt;$BUCKET_NAME&lt;/span&gt;&lt;span class="sh"&gt;/*"
    },
    {
      "Sid": "AllowAdminFullBucketAccess",
      "Effect": "Allow",
      "Principal": {
        "AWS": "arn:aws:iam::&lt;/span&gt;&lt;span class="nv"&gt;$ACCOUNT_B_ID&lt;/span&gt;&lt;span class="sh"&gt;:role/RoleAdmin"
      },
      "Action": "s3:*",
      "Resource": [
        "arn:aws:s3:::&lt;/span&gt;&lt;span class="nv"&gt;$BUCKET_NAME&lt;/span&gt;&lt;span class="sh"&gt;",
        "arn:aws:s3:::&lt;/span&gt;&lt;span class="nv"&gt;$BUCKET_NAME&lt;/span&gt;&lt;span class="sh"&gt;/*"
      ]
    },
    {
      "Sid": "AllowAdminWildcardNoBoundaryFullBucketAccess",
      "Effect": "Allow",
      "Principal": {
        "AWS": "arn:aws:iam::&lt;/span&gt;&lt;span class="nv"&gt;$ACCOUNT_B_ID&lt;/span&gt;&lt;span class="sh"&gt;:role/RoleAdminWildcardNoBoundary"
      },
      "Action": "s3:*",
      "Resource": [
        "arn:aws:s3:::&lt;/span&gt;&lt;span class="nv"&gt;$BUCKET_NAME&lt;/span&gt;&lt;span class="sh"&gt;",
        "arn:aws:s3:::&lt;/span&gt;&lt;span class="nv"&gt;$BUCKET_NAME&lt;/span&gt;&lt;span class="sh"&gt;/*"
      ]
    },
    {
      "Sid": "AllowDiagnosticExplicitNoBoundaryBucketAccess",
      "Effect": "Allow",
      "Principal": {
        "AWS": "arn:aws:iam::&lt;/span&gt;&lt;span class="nv"&gt;$ACCOUNT_B_ID&lt;/span&gt;&lt;span class="sh"&gt;:role/RoleDiagnosticExplicitNoBoundary"
      },
      "Action": [
        "s3:ListBucket",
        "s3:GetBucketPolicy"
      ],
      "Resource": "arn:aws:s3:::&lt;/span&gt;&lt;span class="nv"&gt;$BUCKET_NAME&lt;/span&gt;&lt;span class="sh"&gt;"
    },
    {
      "Sid": "AllowDiagnosticExplicitNoBoundaryObjectAccess",
      "Effect": "Allow",
      "Principal": {
        "AWS": "arn:aws:iam::&lt;/span&gt;&lt;span class="nv"&gt;$ACCOUNT_B_ID&lt;/span&gt;&lt;span class="sh"&gt;:role/RoleDiagnosticExplicitNoBoundary"
      },
      "Action": [
        "s3:GetObject",
        "s3:PutObject",
        "s3:DeleteObject"
      ],
      "Resource": "arn:aws:s3:::&lt;/span&gt;&lt;span class="nv"&gt;$BUCKET_NAME&lt;/span&gt;&lt;span class="sh"&gt;/*"
    }
  ]
}
&lt;/span&gt;&lt;span class="no"&gt;EOF

&lt;/span&gt;aws s3api put-bucket-policy &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--bucket&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$BUCKET_NAME&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--policy&lt;/span&gt; file://bucket-policy.json
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Validación:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;aws s3api get-bucket-policy &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--bucket&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$BUCKET_NAME&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--query&lt;/span&gt; Policy &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--output&lt;/span&gt; text |
jq &lt;span class="nb"&gt;.&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;blockquote&gt;
&lt;h2&gt;
  
  
  En la cuenta real también quedó una entrada anterior para &lt;code&gt;RolePowerUser&lt;/code&gt;. No la utilicé en la comparación final porque los cuatro roles anteriores ya cubrían los casos que necesitaba aislar.
&lt;/h2&gt;
&lt;/blockquote&gt;

&lt;h3&gt;
  
  
  Paso 4: Crear la SCP en AWS Organizations
&lt;/h3&gt;

&lt;p&gt;Desde la management account (o una con permisos de Organizations), se crean las variables:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;export &lt;/span&gt;&lt;span class="nv"&gt;BUCKET_NAME&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"demo-internal-aa-2026"&lt;/span&gt; &lt;span class="c"&gt;# Nombre del bucket seteado al inicio&lt;/span&gt;
&lt;span class="nb"&gt;export &lt;/span&gt;&lt;span class="nv"&gt;OU_ID&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"&amp;lt;OU_ID&amp;gt;"&lt;/span&gt;
&lt;span class="nb"&gt;export &lt;/span&gt;&lt;span class="nv"&gt;SCP_NAME&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"DenyDeleteObjectDemoBucket"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;La SCP niega el borrado de objetos sobre el bucket del lab:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;cat&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;&lt;/span&gt; deny-delete-demo-bucket-scp.json &lt;span class="o"&gt;&amp;lt;&amp;lt;&lt;/span&gt;&lt;span class="no"&gt;EOF&lt;/span&gt;&lt;span class="sh"&gt;
{
  "Version": "2012-10-17",
  "Statement": [
    {
      "Sid": "DenyDeleteObjectOnDemoBucket",
      "Effect": "Deny",
      "Action": [
        "s3:DeleteObject",
        "s3:DeleteObjectVersion"
      ],
      "Resource": "arn:aws:s3:::&lt;/span&gt;&lt;span class="nv"&gt;$BUCKET_NAME&lt;/span&gt;&lt;span class="sh"&gt;/*"
    }
  ]
}
&lt;/span&gt;&lt;span class="no"&gt;EOF
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Se crea la SCP y se adjunta a la OU, donde viven tanto la Cuenta A como la Cuenta B:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nv"&gt;POLICY_ID&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="si"&gt;$(&lt;/span&gt;aws organizations create-policy &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--name&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$SCP_NAME&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--description&lt;/span&gt; &lt;span class="s2"&gt;"Deny object deletion on demo S3 bucket for IAM Access Analyzer lab"&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--type&lt;/span&gt; SERVICE_CONTROL_POLICY &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--content&lt;/span&gt; file://deny-delete-demo-bucket-scp.json &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--query&lt;/span&gt; &lt;span class="s2"&gt;"Policy.PolicySummary.Id"&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--output&lt;/span&gt; text&lt;span class="si"&gt;)&lt;/span&gt;

&lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$POLICY_ID&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;

aws organizations attach-policy &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--policy-id&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$POLICY_ID&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--target-id&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$OU_ID&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Para validar que quedó adjunta:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;aws organizations list-policies-for-target &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--target-id&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$OU_ID&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--filter&lt;/span&gt; SERVICE_CONTROL_POLICY &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--output&lt;/span&gt; table
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;h3&gt;
  
  
  Paso 4.1: Añadir una RCP sobre el bucket
&lt;/h3&gt;

&lt;p&gt;Después de probar la SCP, amplié el ejercicio con una &lt;strong&gt;Resource Control Policy (RCP)&lt;/strong&gt; para bloquear &lt;code&gt;s3:PutObject&lt;/code&gt; directamente sobre el bucket del lab.&lt;/p&gt;

&lt;p&gt;La intención de esta fase ya no era solo ver un deny desde el lado del principal, sino comparar qué pasa cuando el deny viene del lado del recurso.&lt;/p&gt;

&lt;p&gt;La RCP quedó así:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"Version"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"2012-10-17"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"Statement"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"Sid"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"DenyPutObjectOnLabBucket"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"Effect"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Deny"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"Principal"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"*"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"Action"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"s3:PutObject"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"Resource"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"arn:aws:s3:::demo-internal-aa-2026/*"&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Un detalle importante: en RCP el elemento correcto es &lt;strong&gt;Principal&lt;/strong&gt; con mayúscula, y el valor permitido para este tipo de policy es &lt;code&gt;"*"&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;Se creó la policy y se adjuntó a la &lt;strong&gt;OU&lt;/strong&gt;, donde viven tanto la cuenta del bucket como la cuenta de los roles:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;export &lt;/span&gt;&lt;span class="nv"&gt;OU_ID&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"&amp;lt;OU_ID&amp;gt;"&lt;/span&gt;

&lt;span class="nb"&gt;cat&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;&lt;/span&gt; deny-putobject-rcp.json &lt;span class="o"&gt;&amp;lt;&amp;lt;&lt;/span&gt;&lt;span class="no"&gt;EOF&lt;/span&gt;&lt;span class="sh"&gt;
{
  "Version": "2012-10-17",
  "Statement": [
    {
      "Sid": "DenyPutObjectOnLabBucket",
      "Effect": "Deny",
      "Principal": "*",
      "Action": "s3:PutObject",
      "Resource": "arn:aws:s3:::demo-internal-aa-2026/*"
    }
  ]
}
&lt;/span&gt;&lt;span class="no"&gt;EOF

&lt;/span&gt;&lt;span class="nv"&gt;RCP_ID&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="si"&gt;$(&lt;/span&gt;aws organizations create-policy &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--name&lt;/span&gt; &lt;span class="s2"&gt;"DenyPutObjectOnLabBucketRCP"&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--description&lt;/span&gt; &lt;span class="s2"&gt;"RCP to deny PutObject on the lab bucket"&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--type&lt;/span&gt; RESOURCE_CONTROL_POLICY &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--content&lt;/span&gt; file://deny-putobject-rcp.json &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--query&lt;/span&gt; &lt;span class="s2"&gt;"Policy.PolicySummary.Id"&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--output&lt;/span&gt; text&lt;span class="si"&gt;)&lt;/span&gt;

&lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$RCP_ID&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;

aws organizations attach-policy &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--policy-id&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$RCP_ID&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--target-id&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$OU_ID&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Para validar que quedó adjunta:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;aws organizations list-policies-for-target &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--target-id&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$OU_ID&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--filter&lt;/span&gt; RESOURCE_CONTROL_POLICY &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--output&lt;/span&gt; table
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Con esto, el laboratorio quedó así:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;SCP&lt;/strong&gt; → bloquea &lt;code&gt;DeleteObject&lt;/code&gt; y &lt;code&gt;DeleteObjectVersion&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;RCP&lt;/strong&gt; → bloquea &lt;code&gt;PutObject&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Permissions boundary&lt;/strong&gt; → limita el máximo permiso de &lt;code&gt;RoleAdmin&lt;/code&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Y eso dejó una comparación bastante útil porque ya no estaba probando una sola capa organizacional, sino varias a la vez.&lt;/p&gt;




&lt;h3&gt;
  
  
  Paso 5: Crear el IAM Access Analyzer Internal Access
&lt;/h3&gt;

&lt;p&gt;Desde la consola:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;IAM &amp;gt; Access Analyzer &amp;gt; Analyzer settings &amp;gt; Create analyzer
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Configuración:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Analysis:     Resource analysis - Internal access
Zone of trust: Entire organization
Recurso:      arn:aws:s3:::demo-internal-aa-2026
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Un detalle importante: para S3, el recurso debe ser el ARN del bucket.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Correcto:&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;arn:aws:s3:::demo-internal-aa-2026
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Los object ARNs con prefijos o wildcards no están soportados como recurso monitoreado en Internal Access Analyzer. Solo el ARN del bucket sin ruta.&lt;/p&gt;

&lt;p&gt;Después de crear el analizador, esperar hasta que aparezcan los primeros findings. Para realizar la comparación final conviene dejar tiempo suficiente para el reanálisis, como se explica más adelante.&lt;/p&gt;




&lt;h2&gt;
  
  
  Antes de las pruebas: resumen rápido de qué hace cada capa
&lt;/h2&gt;

&lt;p&gt;Si vienes del post anterior, esta tabla resume rápido el alcance de cada mecanismo en este lab:&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Capa&lt;/th&gt;
&lt;th&gt;Qué controla&lt;/th&gt;
&lt;th&gt;Alcance principal&lt;/th&gt;
&lt;th&gt;En este lab&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;IAM policy&lt;/td&gt;
&lt;td&gt;Lo que el usuario o rol puede intentar hacer&lt;/td&gt;
&lt;td&gt;Principal&lt;/td&gt;
&lt;td&gt;Define los permisos base de los cuatro roles&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Bucket policy&lt;/td&gt;
&lt;td&gt;Qué principals pueden acceder al bucket&lt;/td&gt;
&lt;td&gt;Recurso&lt;/td&gt;
&lt;td&gt;Habilita el acceso cross-account desde la Cuenta B&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;SCP&lt;/td&gt;
&lt;td&gt;Máximo permiso disponible para principals en cuentas miembro&lt;/td&gt;
&lt;td&gt;Cuenta / OU / organización&lt;/td&gt;
&lt;td&gt;Bloquea &lt;code&gt;DeleteObject&lt;/code&gt; y &lt;code&gt;DeleteObjectVersion&lt;/code&gt;
&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;RCP&lt;/td&gt;
&lt;td&gt;Máximo permiso disponible sobre recursos en cuentas miembro&lt;/td&gt;
&lt;td&gt;Recurso en cuenta miembro / OU / organización&lt;/td&gt;
&lt;td&gt;Bloquea &lt;code&gt;PutObject&lt;/code&gt; sobre el bucket&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Permissions boundary&lt;/td&gt;
&lt;td&gt;Máximo permiso que puede tener un principal IAM&lt;/td&gt;
&lt;td&gt;Principal IAM&lt;/td&gt;
&lt;td&gt;Limita a &lt;code&gt;RoleAdmin&lt;/code&gt;; no permite &lt;code&gt;GetBucketPolicy&lt;/code&gt;
&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;La forma corta de leerlo es esta:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;IAM&lt;/strong&gt; dice qué intenta hacer el rol&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;bucket policy&lt;/strong&gt; abre el camino cross-account&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;SCP&lt;/strong&gt; limita desde el lado del principal&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;RCP&lt;/strong&gt; limita desde el lado del recurso&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;boundary&lt;/strong&gt; limita el máximo permiso del principal&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;




&lt;h3&gt;
  
  
  Paso 6: Probar acceso asumiendo los roles
&lt;/h3&gt;

&lt;p&gt;Desde CloudShell en la Cuenta B:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;export &lt;/span&gt;&lt;span class="nv"&gt;ACCOUNT_B_ID&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"&amp;lt;ACCOUNT_B_ID&amp;gt;"&lt;/span&gt;
&lt;span class="nb"&gt;export &lt;/span&gt;&lt;span class="nv"&gt;BUCKET_NAME&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"demo-internal-aa-2026"&lt;/span&gt;
&lt;span class="nb"&gt;export &lt;/span&gt;&lt;span class="nv"&gt;PREFIX&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"aa-lab"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Se definen funciones para manejar las credenciales y asumir roles fácilmente:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;clear_role&lt;span class="o"&gt;()&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
  &lt;span class="nb"&gt;unset &lt;/span&gt;AWS_ACCESS_KEY_ID
  &lt;span class="nb"&gt;unset &lt;/span&gt;AWS_SECRET_ACCESS_KEY
  &lt;span class="nb"&gt;unset &lt;/span&gt;AWS_SESSION_TOKEN
  &lt;span class="nb"&gt;unset &lt;/span&gt;AWS_SECURITY_TOKEN
&lt;span class="o"&gt;}&lt;/span&gt;

assume_role&lt;span class="o"&gt;()&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
  &lt;span class="nv"&gt;ROLE_NAME&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$1&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;
  clear_role
  &lt;span class="nv"&gt;CREDS&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="si"&gt;$(&lt;/span&gt;aws sts assume-role &lt;span class="se"&gt;\&lt;/span&gt;
    &lt;span class="nt"&gt;--role-arn&lt;/span&gt; &lt;span class="s2"&gt;"arn:aws:iam::&lt;/span&gt;&lt;span class="nv"&gt;$ACCOUNT_B_ID&lt;/span&gt;&lt;span class="s2"&gt;:role/&lt;/span&gt;&lt;span class="nv"&gt;$ROLE_NAME&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
    &lt;span class="nt"&gt;--role-session-name&lt;/span&gt; &lt;span class="s2"&gt;"lab-&lt;/span&gt;&lt;span class="nv"&gt;$ROLE_NAME&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
    &lt;span class="nt"&gt;--output&lt;/span&gt; json&lt;span class="si"&gt;)&lt;/span&gt;
  &lt;span class="nb"&gt;export &lt;/span&gt;&lt;span class="nv"&gt;AWS_ACCESS_KEY_ID&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="si"&gt;$(&lt;/span&gt;&lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$CREDS&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt; | jq &lt;span class="nt"&gt;-r&lt;/span&gt; &lt;span class="s1"&gt;'.Credentials.AccessKeyId'&lt;/span&gt;&lt;span class="si"&gt;)&lt;/span&gt;
  &lt;span class="nb"&gt;export &lt;/span&gt;&lt;span class="nv"&gt;AWS_SECRET_ACCESS_KEY&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="si"&gt;$(&lt;/span&gt;&lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$CREDS&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt; | jq &lt;span class="nt"&gt;-r&lt;/span&gt; &lt;span class="s1"&gt;'.Credentials.SecretAccessKey'&lt;/span&gt;&lt;span class="si"&gt;)&lt;/span&gt;
  &lt;span class="nb"&gt;export &lt;/span&gt;&lt;span class="nv"&gt;AWS_SESSION_TOKEN&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="si"&gt;$(&lt;/span&gt;&lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$CREDS&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt; | jq &lt;span class="nt"&gt;-r&lt;/span&gt; &lt;span class="s1"&gt;'.Credentials.SessionToken'&lt;/span&gt;&lt;span class="si"&gt;)&lt;/span&gt;
  &lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"Asumiendo rol: &lt;/span&gt;&lt;span class="nv"&gt;$ROLE_NAME&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;
  aws sts get-caller-identity
&lt;span class="o"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h4&gt;
  
  
  Prueba con &lt;code&gt;RoleReadOnly&lt;/code&gt;
&lt;/h4&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;assume_role RoleReadOnly
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;





&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;aws s3 &lt;span class="nb"&gt;ls&lt;/span&gt; &lt;span class="s2"&gt;"s3://&lt;/span&gt;&lt;span class="nv"&gt;$BUCKET_NAME&lt;/span&gt;&lt;span class="s2"&gt;/&lt;/span&gt;&lt;span class="nv"&gt;$PREFIX&lt;/span&gt;&lt;span class="s2"&gt;/"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;





&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;aws s3 &lt;span class="nb"&gt;cp&lt;/span&gt; &lt;span class="s2"&gt;"s3://&lt;/span&gt;&lt;span class="nv"&gt;$BUCKET_NAME&lt;/span&gt;&lt;span class="s2"&gt;/&lt;/span&gt;&lt;span class="nv"&gt;$PREFIX&lt;/span&gt;&lt;span class="s2"&gt;/seed.txt"&lt;/span&gt; readonly-seed.txt
&lt;span class="nb"&gt;cat &lt;/span&gt;readonly-seed.txt
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;





&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"test readonly put"&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;&lt;/span&gt; readonly-put.txt
aws s3 &lt;span class="nb"&gt;cp &lt;/span&gt;readonly-put.txt &lt;span class="s2"&gt;"s3://&lt;/span&gt;&lt;span class="nv"&gt;$BUCKET_NAME&lt;/span&gt;&lt;span class="s2"&gt;/&lt;/span&gt;&lt;span class="nv"&gt;$PREFIX&lt;/span&gt;&lt;span class="s2"&gt;/readonly-put.txt"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Resultado observado:&lt;/strong&gt; &lt;code&gt;PutObject&lt;/code&gt; falla con &lt;strong&gt;AccessDenied&lt;/strong&gt; por &lt;strong&gt;RCP&lt;/strong&gt;.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;aws s3 &lt;span class="nb"&gt;rm&lt;/span&gt; &lt;span class="s2"&gt;"s3://&lt;/span&gt;&lt;span class="nv"&gt;$BUCKET_NAME&lt;/span&gt;&lt;span class="s2"&gt;/&lt;/span&gt;&lt;span class="nv"&gt;$PREFIX&lt;/span&gt;&lt;span class="s2"&gt;/seed.txt"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Resultado observado:&lt;/strong&gt; &lt;code&gt;DeleteObject&lt;/code&gt; falla con &lt;strong&gt;AccessDenied&lt;/strong&gt; por &lt;strong&gt;IAM / identity-based policy&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fut8yi7r1dto2ivzbpg9b.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fut8yi7r1dto2ivzbpg9b.png" alt=" " width="798" height="137"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h4&gt;
  
  
  Prueba con &lt;code&gt;RoleAdmin&lt;/code&gt;
&lt;/h4&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;assume_role RoleAdmin
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;





&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;aws s3 &lt;span class="nb"&gt;ls&lt;/span&gt; &lt;span class="s2"&gt;"s3://&lt;/span&gt;&lt;span class="nv"&gt;$BUCKET_NAME&lt;/span&gt;&lt;span class="s2"&gt;/&lt;/span&gt;&lt;span class="nv"&gt;$PREFIX&lt;/span&gt;&lt;span class="s2"&gt;/"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;





&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;aws s3 &lt;span class="nb"&gt;cp&lt;/span&gt; &lt;span class="s2"&gt;"s3://&lt;/span&gt;&lt;span class="nv"&gt;$BUCKET_NAME&lt;/span&gt;&lt;span class="s2"&gt;/&lt;/span&gt;&lt;span class="nv"&gt;$PREFIX&lt;/span&gt;&lt;span class="s2"&gt;/seed.txt"&lt;/span&gt; admin-seed.txt
&lt;span class="nb"&gt;cat &lt;/span&gt;admin-seed.txt
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;





&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"test admin put"&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;&lt;/span&gt; admin-put.txt
aws s3 &lt;span class="nb"&gt;cp &lt;/span&gt;admin-put.txt &lt;span class="s2"&gt;"s3://&lt;/span&gt;&lt;span class="nv"&gt;$BUCKET_NAME&lt;/span&gt;&lt;span class="s2"&gt;/&lt;/span&gt;&lt;span class="nv"&gt;$PREFIX&lt;/span&gt;&lt;span class="s2"&gt;/admin-put.txt"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Resultado observado:&lt;/strong&gt; &lt;code&gt;PutObject&lt;/code&gt; falla con &lt;strong&gt;AccessDenied&lt;/strong&gt; por &lt;strong&gt;RCP&lt;/strong&gt;.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;aws s3 &lt;span class="nb"&gt;rm&lt;/span&gt; &lt;span class="s2"&gt;"s3://&lt;/span&gt;&lt;span class="nv"&gt;$BUCKET_NAME&lt;/span&gt;&lt;span class="s2"&gt;/&lt;/span&gt;&lt;span class="nv"&gt;$PREFIX&lt;/span&gt;&lt;span class="s2"&gt;/seed.txt"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Resultado observado:&lt;/strong&gt; &lt;code&gt;DeleteObject&lt;/code&gt; falla con &lt;strong&gt;AccessDenied&lt;/strong&gt; por &lt;strong&gt;SCP&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2F64evzaonbkryk3d6f9yp.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2F64evzaonbkryk3d6f9yp.png" alt=" " width="797" height="136"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h4&gt;
  
  
  Prueba específica para validar el permissions boundary
&lt;/h4&gt;

&lt;p&gt;Para comprobar cómo aparecía el boundary en el mensaje de denegación, ejecuté una acción que:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;RoleAdmin&lt;/code&gt; sí tendría por &lt;code&gt;s3:*&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;la bucket policy también permite&lt;/li&gt;
&lt;li&gt;no estaba siendo bloqueada por SCP ni por RCP&lt;/li&gt;
&lt;li&gt;pero el &lt;strong&gt;permissions boundary no permite&lt;/strong&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;La acción elegida fue:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;assume_role RoleAdmin
aws s3api get-bucket-policy &lt;span class="nt"&gt;--bucket&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$BUCKET_NAME&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Y ahí sí apareció la evidencia que faltaba: el &lt;code&gt;AccessDenied&lt;/code&gt; indica que &lt;code&gt;RoleAdmin&lt;/code&gt; &lt;strong&gt;no está autorizado para&lt;/strong&gt; &lt;code&gt;**s3:GetBucketPolicy**&lt;/code&gt; porque &lt;strong&gt;ningún permissions boundary permite esa acción&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fum5bdiyc4qycyvijnak9.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fum5bdiyc4qycyvijnak9.png" alt=" " width="800" height="25"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;El mensaje de &lt;code&gt;AccessDenied&lt;/code&gt; señaló expresamente que ningún permissions boundary permitía &lt;code&gt;s3:GetBucketPolicy&lt;/code&gt;, por lo que confirmó que el boundary participó en la evaluación.&lt;/p&gt;

&lt;h4&gt;
  
  
  Pruebas adicionales: acciones explícitas frente a &lt;code&gt;s3:*&lt;/code&gt;
&lt;/h4&gt;

&lt;p&gt;Para aislar mejor el comportamiento del finding, añadí dos roles sin permissions boundary:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;RoleDiagnosticExplicitNoBoundary&lt;/code&gt;, con las acciones declaradas individualmente.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;RoleAdminWildcardNoBoundary&lt;/code&gt;, con &lt;code&gt;s3:*&lt;/code&gt;.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Ambos tenían acceso concedido tanto por su identity policy como por la bucket policy, y estaban sujetos a la misma SCP y RCP.&lt;/p&gt;

&lt;p&gt;&lt;code&gt;RoleDiagnosticExplicitNoBoundary&lt;/code&gt; tenía permitidos &lt;code&gt;GetBucketPolicy&lt;/code&gt;, &lt;code&gt;GetObject&lt;/code&gt;, &lt;code&gt;PutObject&lt;/code&gt;, &lt;code&gt;DeleteObject&lt;/code&gt; y &lt;code&gt;ListBucket&lt;/code&gt;. Sin embargo, su finding mostró únicamente:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;code&gt;s3:GetBucketPolicy&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;s3:GetObject&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;s3:ListBucket&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Es decir, &lt;code&gt;PutObject&lt;/code&gt; y &lt;code&gt;DeleteObject&lt;/code&gt; no aparecieron, tal como esperaba después de aplicar la RCP y la SCP.&lt;/p&gt;

&lt;p&gt;En cambio, &lt;code&gt;RoleAdminWildcardNoBoundary&lt;/code&gt; tenía &lt;code&gt;s3:*&lt;/code&gt; y su finding mantuvo una lista amplia de acciones, incluyendo:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;code&gt;s3:PutObject&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;s3:DeleteObject&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;s3:DeleteObjectVersion&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Esto ocurrió aunque el mismo finding mostraba las restricciones RCP y SCP como &lt;code&gt;APPLIED&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2F77ies7b61rm2lxqgdcr9.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2F77ies7b61rm2lxqgdcr9.png" alt=" " width="800" height="386"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fn3ni24hnddukk6mpzfmy.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fn3ni24hnddukk6mpzfmy.png" alt=" " width="355" height="300"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fk3u3thnpnf5w3poe7xnt.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fk3u3thnpnf5w3poe7xnt.png" alt=" " width="359" height="335"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;En la captura se observa que el finding de &lt;code&gt;RoleAdminWildcardNoBoundary&lt;/code&gt; conserva &lt;code&gt;PutObject&lt;/code&gt;, &lt;code&gt;DeleteObject&lt;/code&gt; y &lt;code&gt;DeleteObjectVersion&lt;/code&gt;, aunque las restricciones SCP y RCP aparecen como aplicadas.&lt;/p&gt;

&lt;p&gt;Para comprobar el acceso real, asumí ambos roles y ejecuté las cuatro operaciones principales.&lt;/p&gt;

&lt;p&gt;En los dos casos:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;ListBucket&lt;/code&gt; y &lt;code&gt;GetObject&lt;/code&gt; fueron permitidos.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;PutObject&lt;/code&gt; devolvió &lt;code&gt;AccessDenied&lt;/code&gt; por un explicit deny de la RCP.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;DeleteObject&lt;/code&gt; devolvió &lt;code&gt;AccessDenied&lt;/code&gt; por un explicit deny de la SCP.&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Rol&lt;/th&gt;
&lt;th&gt;Forma de declarar permisos&lt;/th&gt;
&lt;th&gt;Resultado esperado&lt;/th&gt;
&lt;th&gt;Finding observado&lt;/th&gt;
&lt;th&gt;Runtime&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;RoleDiagnosticExplicitNoBoundary&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Acciones explícitas&lt;/td&gt;
&lt;td&gt;Sin Put ni Delete&lt;/td&gt;
&lt;td&gt;No muestra Put ni Delete&lt;/td&gt;
&lt;td&gt;List y Get permitidos; Put denegado por RCP y Delete por SCP&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;RoleAdminWildcardNoBoundary&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;s3:*&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Sin Put, Delete ni DeleteObjectVersion&lt;/td&gt;
&lt;td&gt;Conserva esas acciones&lt;/td&gt;
&lt;td&gt;List y Get permitidos; Put denegado por RCP y Delete por SCP&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;La comparación muestra una diferencia observable entre ambos findings. No determina por sí sola la causa interna del comportamiento, pero sí permite contrastar lo reportado por el analyzer con el resultado real de las APIs.&lt;/p&gt;




&lt;h3&gt;
  
  
  Ver los findings en IAM Access Analyzer
&lt;/h3&gt;

&lt;p&gt;Con los roles ya creados y el analizador activo, el siguiente paso es revisar qué detectó IAM Access Analyzer sobre el bucket.&lt;/p&gt;

&lt;p&gt;En la consola de IAM, dentro de &lt;strong&gt;Access Analyzer&lt;/strong&gt;, puedes ver todos los analizadores activos. En este lab, el que interesa es el de &lt;strong&gt;internal access&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fo8f24s9r642akbb99dcd.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fo8f24s9r642akbb99dcd.png" alt=" " width="799" height="457"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Al entrar al analizador y seleccionar el bucket, aparece una lista de findings. Y aquí viene un detalle importante: no vas a ver solo los roles del laboratorio. También pueden aparecer roles internos de la misma cuenta dueña del bucket, como roles administrativos, roles de AWS IAM Identity Center (SSO) o service roles.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2F8n9xopq5vc3cymf41fh0.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2F8n9xopq5vc3cymf41fh0.png" alt=" " width="800" height="534"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Si esperabas encontrar únicamente los cuatro roles creados para el laboratorio, aquí es donde te das cuenta de que olvidaste considerar algo más: &lt;strong&gt;el analizador muestra acceso efectivo al recurso&lt;/strong&gt;, no solo lo que agregaste manualmente en la bucket policy del lab.&lt;/p&gt;

&lt;p&gt;Y acá está una de las claves del escenario: &lt;strong&gt;los principals de la misma cuenta del bucket no necesitan estar expresamente nombrados en la bucket policy para tener acceso&lt;/strong&gt;. Si un rol o usuario de la cuenta propietaria ya tiene permisos por su propia IAM identity policy, puede acceder igual. AWS lo documenta de forma explícita para S3 aquí: &lt;a href="https://docs.aws.amazon.com/AmazonS3/latest/userguide/example-bucket-policies.html" rel="noopener noreferrer"&gt;Granting access to an IAM principal in the same account does not require updating the bucket policy&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;La bucket policy se vuelve realmente obligatoria cuando quieres habilitar acceso &lt;strong&gt;cross-account&lt;/strong&gt;, como sí pasa con los roles del laboratorio.&lt;/p&gt;

&lt;h3&gt;
  
  
  Qué muestra la lista de findings
&lt;/h3&gt;

&lt;p&gt;En la vista resumida, estas son las columnas más útiles:&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Campo&lt;/th&gt;
&lt;th&gt;Qué significa&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;ID de resultado&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Identificador único del finding&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Recurso&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;El bucket analizado&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Cuenta del propietario del recurso&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;La cuenta dueña del bucket&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Entidad principal&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;El rol o principal que tiene acceso&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Condición&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Si el acceso depende de alguna condición&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Compartido a través de&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Qué capa origina el acceso, por ejemplo bucket policy o bucket ACL&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Nivel de acceso&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Categorías generales de acciones: Read, List, Write, Permissions, Tagging&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Restricción RCP&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Si el analizador consideró una Resource Control Policy&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Restricción SCP&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Si el analizador consideró una Service Control Policy&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Estado&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Si el finding sigue activo o fue archivado&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;Dos columnas valen especial atención en este lab:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Restricción SCP = Aplicado&lt;/strong&gt;&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Restricción RCP = Aplicado&lt;/strong&gt;&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Eso no significa por sí solo que una acción específica como &lt;code&gt;s3:DeleteObject&lt;/code&gt; o &lt;code&gt;s3:PutObject&lt;/code&gt; haya desaparecido de la lista. Lo que sí significa es que &lt;strong&gt;esas capas fueron consideradas en la evaluación&lt;/strong&gt;.&lt;/p&gt;

&lt;h3&gt;
  
  
  Ver el detalle de un finding
&lt;/h3&gt;

&lt;p&gt;La lista resumida ayuda, pero el valor real aparece cuando entras al detalle de cada finding.&lt;/p&gt;

&lt;p&gt;En el caso de &lt;code&gt;RoleReadOnly&lt;/code&gt;, el resultado es bastante limpio:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Principal:&lt;/strong&gt; &lt;code&gt;Account B / RoleReadOnly&lt;/code&gt;&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Cuenta del principal:&lt;/strong&gt; &lt;code&gt;Account B&lt;/code&gt;&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Compartido a través de:&lt;/strong&gt; &lt;code&gt;Política del bucket&lt;/code&gt;&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Acceso reportado por el finding:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;Read&lt;/code&gt; → &lt;code&gt;s3:GetObject&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;List&lt;/code&gt; → &lt;code&gt;s3:ListBucket&lt;/code&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Ese finding ya no muestra solo categorías amplias. Muestra directamente las acciones que el analizador está reportando para ese principal sobre el bucket.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fkfsev5w5jpn50g4wqgtc.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fkfsev5w5jpn50g4wqgtc.png" alt=" " width="799" height="357"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fzirin4twk86zfrfn74ys.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fzirin4twk86zfrfn74ys.png" alt=" " width="800" height="542"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  Qué demuestra esto sobre la SCP, la RCP y el permissions boundary
&lt;/h3&gt;

&lt;p&gt;Acá apareció una de las partes más interesantes del ejercicio.&lt;/p&gt;

&lt;p&gt;Al comienzo solo tenía &lt;code&gt;RoleReadOnly&lt;/code&gt; y &lt;code&gt;RoleAdmin&lt;/code&gt;, pero después añadí dos roles para comparar mejor el resultado:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;RoleDiagnosticExplicitNoBoundary&lt;/code&gt;, con las acciones declaradas individualmente.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;RoleAdminWildcardNoBoundary&lt;/code&gt;, con &lt;code&gt;s3:*&lt;/code&gt; y sin permissions boundary.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Los cuatro roles estaban sujetos a la misma SCP y RCP, pero los findings no se presentaron exactamente igual.&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Rol&lt;/th&gt;
&lt;th&gt;Configuración&lt;/th&gt;
&lt;th&gt;Finding observado&lt;/th&gt;
&lt;th&gt;Resultado runtime&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;RoleReadOnly&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;
&lt;code&gt;GetObject&lt;/code&gt; y &lt;code&gt;ListBucket&lt;/code&gt;
&lt;/td&gt;
&lt;td&gt;Muestra &lt;code&gt;GetObject&lt;/code&gt; y &lt;code&gt;ListBucket&lt;/code&gt;
&lt;/td&gt;
&lt;td&gt;Lectura permitida; escritura y borrado denegados&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;RoleDiagnosticExplicitNoBoundary&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Get, Put, Delete, List y GetBucketPolicy declarados explícitamente&lt;/td&gt;
&lt;td&gt;No muestra &lt;code&gt;PutObject&lt;/code&gt; ni &lt;code&gt;DeleteObject&lt;/code&gt;
&lt;/td&gt;
&lt;td&gt;List y Get permitidos; Put denegado por RCP y Delete por SCP&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;RoleAdmin&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;
&lt;code&gt;s3:*&lt;/code&gt; limitado por un permissions boundary&lt;/td&gt;
&lt;td&gt;Muestra únicamente &lt;code&gt;GetObject&lt;/code&gt; y &lt;code&gt;ListBucket&lt;/code&gt;
&lt;/td&gt;
&lt;td&gt;Put denegado por RCP, Delete por SCP y GetBucketPolicy por boundary&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;RoleAdminWildcardNoBoundary&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;
&lt;code&gt;s3:*&lt;/code&gt; sin permissions boundary&lt;/td&gt;
&lt;td&gt;Mantiene &lt;code&gt;PutObject&lt;/code&gt;, &lt;code&gt;DeleteObject&lt;/code&gt; y &lt;code&gt;DeleteObjectVersion&lt;/code&gt;
&lt;/td&gt;
&lt;td&gt;List y Get permitidos; Put denegado por RCP y Delete por SCP&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;Tomando como referencia las cuatro operaciones principales del laboratorio (&lt;code&gt;ListBucket&lt;/code&gt;, &lt;code&gt;GetObject&lt;/code&gt;, &lt;code&gt;PutObject&lt;/code&gt; y &lt;code&gt;DeleteObject&lt;/code&gt;), en los tres primeros casos el finding coincidió con el conjunto de acciones que esperaba después de considerar las distintas capas.&lt;/p&gt;

&lt;p&gt;La diferencia apareció con &lt;code&gt;RoleAdminWildcardNoBoundary&lt;/code&gt;. Su finding seguía incluyendo acciones que las pruebas runtime confirmaron como denegadas:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;PutObject&lt;/code&gt; devolvió &lt;code&gt;AccessDenied&lt;/code&gt; por un explicit deny de la RCP.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;DeleteObject&lt;/code&gt; devolvió &lt;code&gt;AccessDenied&lt;/code&gt; por un explicit deny de la SCP.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Además, en los findings las dos restricciones aparecían como:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;resourceControlPolicyRestriction: APPLIED
serviceControlPolicyRestriction: APPLIED
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Para todo el set de pruebas, después de completar los cambios esperé al menos 24 horas antes de volver a revisar los findings. También confirmé mediante &lt;code&gt;get-finding-v2&lt;/code&gt; que los resultados habían sido reanalizados.&lt;/p&gt;

&lt;p&gt;De esta manera evité comparar findings que todavía pudieran estar pendientes de actualización.&lt;/p&gt;

&lt;p&gt;Esto no significa que la SCP o la RCP hayan fallado. Las pruebas runtime muestran precisamente lo contrario: ambas políticas bloquearon las operaciones.&lt;/p&gt;

&lt;p&gt;Lo que sí puedo documentar es una diferencia entre las acciones reportadas por el finding y las acciones que el rol pudo ejecutar realmente en este escenario específico.&lt;/p&gt;

&lt;p&gt;Por eso, mi lectura final del laboratorio es esta:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;&lt;strong&gt;El finding es muy útil para identificar principals, rutas de acceso y acciones reportadas sobre el recurso.&lt;/strong&gt;&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Las columnas SCP y RCP permiten saber si esas capas participaron en la evaluación.&lt;/strong&gt;&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Cuando necesitas validar una API concreta, la prueba runtime sigue siendo una evidencia importante.&lt;/strong&gt;&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Si el finding y runtime no coinciden, conviene revisar directamente IAM, bucket policy, boundary, SCP, RCP y confirmar que el analyzer tuvo tiempo suficiente para actualizarse antes de sacar una conclusión.&lt;/strong&gt;&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;No intento determinar desde fuera cuál es la causa interna de esta diferencia ni generalizar el resultado a todos los servicios o configuraciones. El objetivo es dejar la configuración y la evidencia del laboratorio de forma reproducible.&lt;/p&gt;

&lt;p&gt;La referencia oficial utilizada para interpretar los campos &lt;code&gt;APPLIED&lt;/code&gt; es: &lt;a href="https://docs.aws.amazon.com/access-analyzer/latest/APIReference/API_InternalAccessDetails.html" rel="noopener noreferrer"&gt;InternalAccessDetails – AWS IAM Access Analyzer API&lt;/a&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  Por qué aparecen otros roles aparte de los del lab
&lt;/h3&gt;

&lt;p&gt;También aparecen findings para roles internos como administradores federados, roles de Control Tower y service roles de la propia cuenta propietaria del bucket.&lt;/p&gt;

&lt;p&gt;Eso &lt;strong&gt;no significa&lt;/strong&gt; que la bucket policy del lab les haya dado acceso a todos ellos.&lt;/p&gt;

&lt;p&gt;Lo que significa es que el bucket también es accesible por &lt;strong&gt;principals internos de la misma cuenta propietaria del recurso&lt;/strong&gt;, y Access Analyzer los muestra porque su trabajo no es enseñarte solo el acceso cross-account del ejercicio, sino el &lt;strong&gt;acceso efectivo total dentro del alcance del analizador&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;Dicho de otra forma:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Account A&lt;/strong&gt; es la cuenta dueña del bucket.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Account B&lt;/strong&gt; es la cuenta donde viven los cuatro roles utilizados en el laboratorio.&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Los roles del lab explican el escenario &lt;strong&gt;cross-account&lt;/strong&gt;.&lt;br&gt;&lt;br&gt;
Los otros findings muestran acceso &lt;strong&gt;intra-account&lt;/strong&gt; de roles que ya existen en &lt;strong&gt;Account A&lt;/strong&gt; y que tienen permisos por otras capas del entorno.&lt;/p&gt;

&lt;p&gt;En un laboratorio pequeño esto ya se vuelve fácil de perder de vista. Ahora imagina el mismo problema en una organización real con decenas o cientos de cuentas, miles de roles, roles federados de SSO, service roles, roles de Control Tower, políticas administradas, inline policies, bucket policies, ACLs, SCPs, RCPs, permissions boundaries y además múltiples recursos aparte de un solo bucket. En ese punto, responder “quién puede acceder realmente a qué” deja de ser una revisión manual y pasa a ser un problema de correlación entre varias capas de permisos.&lt;/p&gt;

&lt;p&gt;Y esa es precisamente la complicación: el acceso efectivo no vive en una sola policy. Sale de la combinación de identity policies, resource policies, permisos intra-account, permisos cross-account y restricciones organizacionales. Si revisas solo una bucket policy, puedes pensar que entiendes el acceso del recurso, pero en realidad te puedes estar perdiendo principals internos de la misma cuenta, roles heredados del entorno, service roles o accesos que siguen siendo válidos por otras rutas. Mientras más cuentas, roles y recursos agregas, más difícil se vuelve distinguir entre acceso esperado, acceso heredado, acceso indirecto y acceso realmente riesgoso.&lt;/p&gt;

&lt;p&gt;Por eso herramientas como IAM Access Analyzer toman valor en escenarios grandes: no porque reemplacen entender IAM, sino porque ayudan a reducir la complejidad operativa cuando ya no estás mirando cuatro roles de laboratorio, sino cientos o miles de identidades con permisos cruzados sobre muchos recursos.&lt;/p&gt;




&lt;h4&gt;
  
  
  El botón Archivar: para qué sirve
&lt;/h4&gt;

&lt;p&gt;Archivar un finding no modifica el acceso ni cambia las policies. Solo cambia la forma en que administras ese resultado dentro del analyzer.&lt;/p&gt;

&lt;p&gt;Cuando archivas un finding:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;deja de mostrarse entre los findings activos;&lt;/li&gt;
&lt;li&gt;permanece disponible como archivado;&lt;/li&gt;
&lt;li&gt;puedes utilizar archive rules para archivar automáticamente nuevos findings que coincidan con criterios definidos;&lt;/li&gt;
&lt;li&gt;al crear una regla puedes elegir aplicarla solo a nuevos findings o también archivar findings activos existentes.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Si desaparece la ruta de acceso, el finding pasa a &lt;code&gt;Resolved&lt;/code&gt;. Si la ruta cambia de forma relevante, Access Analyzer puede resolver el finding anterior y generar uno nuevo.&lt;/p&gt;




&lt;h3&gt;
  
  
  Resultados del laboratorio
&lt;/h3&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Rol&lt;/th&gt;
&lt;th&gt;ListBucket&lt;/th&gt;
&lt;th&gt;GetObject&lt;/th&gt;
&lt;th&gt;PutObject&lt;/th&gt;
&lt;th&gt;DeleteObject&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;RoleReadOnly&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;✅ Permitido&lt;/td&gt;
&lt;td&gt;✅ Permitido&lt;/td&gt;
&lt;td&gt;❌ RCP&lt;/td&gt;
&lt;td&gt;❌ IAM&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;RoleAdmin&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;✅ Permitido&lt;/td&gt;
&lt;td&gt;✅ Permitido&lt;/td&gt;
&lt;td&gt;❌ RCP&lt;/td&gt;
&lt;td&gt;❌ SCP&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;RoleDiagnosticExplicitNoBoundary&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;✅ Permitido&lt;/td&gt;
&lt;td&gt;✅ Permitido&lt;/td&gt;
&lt;td&gt;❌ RCP&lt;/td&gt;
&lt;td&gt;❌ SCP&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;RoleAdminWildcardNoBoundary&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;✅ Permitido&lt;/td&gt;
&lt;td&gt;✅ Permitido&lt;/td&gt;
&lt;td&gt;❌ RCP&lt;/td&gt;
&lt;td&gt;❌ SCP&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;La tabla muestra únicamente las llamadas realmente ejecutadas. La comparación de los findings se encuentra en la sección anterior.&lt;/p&gt;




&lt;h3&gt;
  
  
  Qué demuestra este lab
&lt;/h3&gt;

&lt;p&gt;Para que una acción cross-account funcione en este escenario se necesitan estas condiciones:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Una identity policy en el rol de la Cuenta B que permita la acción.&lt;/li&gt;
&lt;li&gt;Una bucket policy en la Cuenta A que conceda esa acción al rol externo.&lt;/li&gt;
&lt;li&gt;Que no exista un explicit deny aplicable a esa acción y a ese recurso.&lt;/li&gt;
&lt;li&gt;Si el rol tiene un permissions boundary, que la acción esté incluida dentro de su máximo permitido.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Esto no significa que no puedan existir SCP o RCP. En este laboratorio existen ambas, pero bloquean operaciones concretas: la SCP bloquea &lt;code&gt;DeleteObject&lt;/code&gt; y &lt;code&gt;DeleteObjectVersion&lt;/code&gt;, mientras que la RCP bloquea &lt;code&gt;PutObject&lt;/code&gt;. Las demás acciones continúan disponibles según las otras policies aplicables.&lt;/p&gt;

&lt;p&gt;La primera parte del lab muestra esto con SCP: el rol tiene permiso IAM, la bucket policy también se lo da, pero el explicit deny organizacional gana.&lt;/p&gt;

&lt;p&gt;La ampliación con RCP refuerza la misma idea desde el lado del recurso: aunque la ruta de acceso exista, el control organizacional sigue pudiendo cortar la operación en runtime.&lt;/p&gt;

&lt;p&gt;Y la prueba con &lt;code&gt;GetBucketPolicy&lt;/code&gt; añade una tercera lección importante: un &lt;strong&gt;permissions boundary&lt;/strong&gt; también puede participar como capa limitante y aparecer expresamente identificado en el mensaje de &lt;code&gt;AccessDenied&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;Lo importante es que el lab no solo muestra el deny, sino también algo más útil para producción: &lt;strong&gt;te obliga a entender cómo leer el finding y cómo interpretar los errores runtime sin asumir que uno de los dos es un espejo perfecto del otro&lt;/strong&gt;.&lt;/p&gt;




&lt;h2&gt;
  
  
  Antes de habilitarlo en producción: el pricing
&lt;/h2&gt;

&lt;p&gt;Acá hay algo que casi ningún tutorial menciona y que conviene saber antes de habilitar esto en producción.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;IAM Access Analyzer Internal Access no es gratuito&lt;/strong&gt; para el análisis de permisos efectivos.&lt;/p&gt;

&lt;p&gt;Algunas consideraciones prácticas:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;No lo habilites sobre todos los recursos por default.&lt;/strong&gt; Empieza por los buckets que contengan datos sensibles o regulados.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Usa etiquetas para identificar recursos críticos&lt;/strong&gt; (&lt;code&gt;DataClassification: Confidential&lt;/code&gt;, por ejemplo) y limita el scope del analizador a esos recursos.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Tiene más sentido en recursos específicos de alto riesgo&lt;/strong&gt;: buckets con datos PCI, repositorios de logs de seguridad, buckets compartidos cross-account, tablas de DynamoDB, snapshots de bases de datos RDS y snapshots de clústeres RDS.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Para el resto, IAM Access Advisor puede ayudar a priorizar una revisión manual periódica y reducir el número de recursos sobre los que necesitas habilitar Internal Access Analyzer.&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;La recomendación concreta: define primero qué es un recurso crítico en tu organización, aplica tags de clasificación de datos, y usa Access Analyzer Internal Access solo sobre ese subconjunto. No sobre todo.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Nota práctica:&lt;/strong&gt; si pensaste en activarlo un momento y luego borrarlo, ojo: en mi caso el cobro se reflejó el mismo día que lo activé y por la unidad mensual completa del recurso monitoreado. Eso ya de por sí te obliga a pensar mejor el experimento antes de habilitarlo.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fqqxvbsd25l2tebnovd7w.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fqqxvbsd25l2tebnovd7w.png" alt=" " width="800" height="382"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Y acá sumaría otra recomendación basada en lo que me pasó en este lab: &lt;strong&gt;antes de habilitarlo sobre recursos críticos, entiende bien qué tipo de preguntas quieres responder con él&lt;/strong&gt;.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;Si tu pregunta es &lt;strong&gt;“quién puede llegar a este recurso y por qué camino”&lt;/strong&gt;, el analyzer te aporta muchísimo.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Si tu pregunta es &lt;strong&gt;“esta API exacta quedó efectivamente bloqueada por esta capa organizacional o de identidad”&lt;/strong&gt;, entonces probablemente también vas a querer hacer validación runtime para no llevarte sorpresas al interpretar el finding.&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;




&lt;h2&gt;
  
  
  Perspectiva Red Team
&lt;/h2&gt;

&lt;p&gt;Access Analyzer no es solamente útil para equipos defensivos, pero esta parte necesita contexto.&lt;/p&gt;

&lt;p&gt;Un usuario o rol necesita permisos para consultar el analyzer y sus findings. Además, las llamadas realizadas desde la consola o mediante las APIs de IAM Access Analyzer se registran en AWS CloudTrail.&lt;/p&gt;

&lt;p&gt;Por tanto, no es correcto asumir que cualquier rol comprometido en una cuenta miembro puede consultar el analyzer organizacional o que su uso sea invisible.&lt;/p&gt;

&lt;p&gt;En un ejercicio Red Team interno y autorizado, una identidad que ya tenga acceso al analyzer podría utilizar los findings para:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Revisar rutas de acceso sin llamar directamente al recurso objetivo.&lt;/li&gt;
&lt;li&gt;Priorizar relaciones cross-account relevantes.&lt;/li&gt;
&lt;li&gt;Comparar acceso reportado frente a acceso ejecutable.&lt;/li&gt;
&lt;li&gt;Evaluar el impacto de SCP, RCP y permissions boundaries.&lt;/li&gt;
&lt;/ol&gt;




&lt;h2&gt;
  
  
  Lo que Access Analyzer muestra (y lo que no)
&lt;/h2&gt;

&lt;p&gt;Para dejarlo claro:&lt;/p&gt;

&lt;p&gt;✅ Qué identidades tienen acceso a un recurso específico&lt;br&gt;&lt;br&gt;
✅ Qué capas participaron en la evaluación, incluyendo IAM, resource policies, SCPs y RCPs&lt;br&gt;&lt;br&gt;
✅ Findings exportables para evidencia de auditoría&lt;br&gt;&lt;br&gt;
✅ Visibilidad centralizada a escala de organización desde la cuenta que administra el analyzer&lt;/p&gt;

&lt;p&gt;Y también algo que este lab me dejó mucho más claro:&lt;/p&gt;

&lt;p&gt;⚠️ No siempre lo usaría como única evidencia para afirmar que una API concreta desapareció del acceso efectivo solo porque una SCP o una RCP la bloquean en runtime&lt;br&gt;&lt;br&gt;
⚠️ El mensaje de AccessDenied no siempre te va a mostrar todas las capas que podrían estar contribuyendo a la denegación&lt;br&gt;&lt;br&gt;
⚠️ Para denegaciones puntuales por capa organizacional o por boundary, conviene complementar con una prueba runtime controlada&lt;/p&gt;




&lt;h2&gt;
  
  
  Conclusión
&lt;/h2&gt;

&lt;p&gt;La pregunta con la que empezó esta serie era:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;¿Quién puede acceder realmente a tus recursos en AWS?&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Después de este lab, la respuesta práctica sigue siendo que no puedes resolverlo revisando una sola policy.&lt;/p&gt;

&lt;p&gt;IAM Access Analyzer Internal Access aporta valor para descubrir principals, acciones y rutas de acceso sobre recursos críticos. También permite centralizar los resultados a nivel organizacional y consultar los findings mediante API.&lt;/p&gt;

&lt;p&gt;El laboratorio dejó además una observación concreta: tomando como referencia las cuatro operaciones principales, en tres casos controlados el finding coincidió con el conjunto de acciones esperado. En el rol con &lt;code&gt;s3:*&lt;/code&gt; sin permissions boundary, el finding siguió mostrando &lt;code&gt;PutObject&lt;/code&gt;, &lt;code&gt;DeleteObject&lt;/code&gt; y &lt;code&gt;DeleteObjectVersion&lt;/code&gt;, aunque las restricciones aparecían como &lt;code&gt;APPLIED&lt;/code&gt;. Las pruebas runtime confirmaron que &lt;code&gt;PutObject&lt;/code&gt; era denegado por la RCP y &lt;code&gt;DeleteObject&lt;/code&gt; por la SCP.&lt;/p&gt;

&lt;p&gt;No voy a especular sobre la causa exacta. Lo que sí puedo hacer es dejar la configuración, los findings y las pruebas runtime para que el resultado sea reproducible.&lt;/p&gt;

&lt;p&gt;Y debido a que Internal Access Analyzer es una capacidad pagada, también recomiendo pensar bien qué preguntas quieres responder antes de habilitarlo. Si buscas identificar quién puede acceder a un bucket crítico y por qué ruta, resulta especialmente útil. Pero si solo necesitas validar una API puntual, quizá una revisión de policies y una prueba runtime controlada sean suficientes.&lt;/p&gt;

&lt;p&gt;El costo se calcula por recurso monitoreado, por analyzer y por región, así que habilitarlo sin definir previamente el alcance puede terminar generando un gasto mayor al esperado. En producción empezaría por recursos realmente críticos, como buckets con datos sensibles, logs de seguridad o recursos compartidos entre cuentas, y ampliaría el alcance solo cuando exista una necesidad clara.&lt;/p&gt;

&lt;p&gt;Para mí, esa es la lectura más útil del ejercicio:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;usa el analyzer para ganar visibilidad;&lt;/li&gt;
&lt;li&gt;entiende qué significa cada campo;&lt;/li&gt;
&lt;li&gt;valida las APIs críticas cuando necesites una respuesta puntual;&lt;/li&gt;
&lt;li&gt;define el alcance antes de activarlo;&lt;/li&gt;
&lt;li&gt;evita interpretar una sola pantalla sin revisar las demás capas.&lt;/li&gt;
&lt;/ul&gt;




&lt;p&gt;💡 &lt;strong&gt;¿Encontraste algún acceso que no esperabas al analizar tus recursos?&lt;/strong&gt; Ese suele ser el momento en que este tipo de herramientas pasan de “interesante” a “necesaria”.&lt;/p&gt;

&lt;p&gt;🔗 Si llegaste directo aquí, te recomiendo leer la Parte 1 primero para entender el contexto.&lt;/p&gt;

&lt;p&gt;📌 Y si quieres el contexto de SCPs, RCPs y boundaries desde cero, tengo un post anterior donde explico cada tipo de política con ejemplos concretos.&lt;/p&gt;

&lt;h1&gt;
  
  
  AWS #CloudSecurity #IAM #AccessAnalyzer #SCPs #RCPs #PermissionsBoundary #CloudGovernance #AWSOrganizations #LeastPrivilege #RedTeam
&lt;/h1&gt;

</description>
      <category>aws</category>
      <category>security</category>
      <category>cybersecurity</category>
      <category>infrastructure</category>
    </item>
    <item>
      <title>🛡️ AWS IAM Access Analyzer — Parte 2/4</title>
      <dc:creator>Terry Quispe Paniagua</dc:creator>
      <pubDate>Fri, 03 Jul 2026 00:46:28 +0000</pubDate>
      <link>https://dev.to/terry_cloud/-aws-iam-access-analyzer-parte-24-2lab</link>
      <guid>https://dev.to/terry_cloud/-aws-iam-access-analyzer-parte-24-2lab</guid>
      <description>&lt;h2&gt;
  
  
  External Access: el analizador sin costo adicional que deberías tener activo en todas tus cuentas
&lt;/h2&gt;




&lt;p&gt;En AWS, el acceso no depende de una sola policy. Entre identity policies, resource-based policies, trust policies, SCPs, RCPs, boundaries y configuraciones por servicio, saber si un recurso está expuesto puede volverse confuso rápido.&lt;/p&gt;

&lt;p&gt;Antes de entrar al análisis de permisos efectivos internos, conviene empezar por el analyzer más básico y más urgente: &lt;strong&gt;External Access Analyzer&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;Este post es sobre ese primer baseline: detectar recursos que están accesibles desde fuera de tu zona de confianza. No tiene costo adicional por recurso analizado, se habilita por región y cubre automáticamente los recursos soportados en esa región. Su objetivo es detectar algo muy concreto: recursos accesibles desde fuera de la zona de confianza que defines.&lt;/p&gt;




&lt;h2&gt;
  
  
  ¿Qué hace exactamente el External Access Analyzer?
&lt;/h2&gt;

&lt;p&gt;El External Access Analyzer genera findings cuando detecta que un recurso dentro de tu zona de confianza puede ser accedido por un principal externo. Esa zona de confianza puede ser una cuenta individual o toda una AWS Organization.&lt;/p&gt;

&lt;p&gt;Lo que evalúa depende del tipo de recurso soportado. En términos prácticos, revisa políticas o configuraciones como:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;bucket policies y ACLs de S3&lt;/li&gt;
&lt;li&gt;trust policies de roles IAM&lt;/li&gt;
&lt;li&gt;key policies y grants de KMS&lt;/li&gt;
&lt;li&gt;queue policies de SQS&lt;/li&gt;
&lt;li&gt;topic policies de SNS&lt;/li&gt;
&lt;li&gt;function policies de Lambda&lt;/li&gt;
&lt;li&gt;resource policies de Secrets Manager, ECR, EFS, DynamoDB, entre otros&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Si la policy de un recurso tiene un &lt;code&gt;Principal: "*"&lt;/code&gt;, una cuenta externa, un usuario federado externo o una condición que permite acceso fuera de tu zona de confianza, el analizador puede generar un finding.&lt;/p&gt;

&lt;h3&gt;
  
  
  Qué tipos de acceso externo detecta
&lt;/h3&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Tipo de acceso&lt;/th&gt;
&lt;th&gt;Ejemplo&lt;/th&gt;
&lt;th&gt;Finding generado&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Acceso público&lt;/td&gt;
&lt;td&gt;
&lt;code&gt;"Principal": "*"&lt;/code&gt; sin condición restrictiva&lt;/td&gt;
&lt;td&gt;✅ Sí&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Acceso cross-account a otra cuenta de la misma org&lt;/td&gt;
&lt;td&gt;ARN de cuenta en la misma organización&lt;/td&gt;
&lt;td&gt;Solo si la zona de trust es la cuenta individual&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Acceso cross-account fuera de la org&lt;/td&gt;
&lt;td&gt;ARN de cuenta externa&lt;/td&gt;
&lt;td&gt;✅ Sí&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Acceso restringido por organización&lt;/td&gt;
&lt;td&gt;&lt;code&gt;Condition: { "aws:PrincipalOrgID": "o-xxxx" }&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Normalmente no, si la zona de trust es esa organización&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Acceso mediante federación externa&lt;/td&gt;
&lt;td&gt;SAML/OIDC con IdP externo o federado&lt;/td&gt;
&lt;td&gt;✅ Sí, si queda fuera de la zona de confianza&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;La diferencia entre configurar la zona de confianza como &lt;strong&gt;cuenta&lt;/strong&gt; versus &lt;strong&gt;organización&lt;/strong&gt; importa bastante.&lt;/p&gt;

&lt;p&gt;Si la defines como organización, los accesos cross-account dentro de la misma AWS Organization no generan findings porque se consideran internos a la zona de confianza. Si la defines como cuenta, cualquier acceso cross-account, incluso desde una cuenta hermana de la misma organización, puede generar finding.&lt;/p&gt;




&lt;h2&gt;
  
  
  Por qué este analizador importa antes que cualquier otro
&lt;/h2&gt;

&lt;p&gt;El Internal Access Analyzer tiene costo porque se cobra por recurso monitoreado. El Unused Access Analyzer también tiene costo porque se cobra por rol o usuario analizado. External Access Analyzer, en cambio, no tiene costo adicional y cubre automáticamente los recursos soportados de la región donde se habilita.&lt;/p&gt;

&lt;p&gt;El problema que detecta es frecuente y de alto impacto:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;bucket S3 con &lt;code&gt;"Principal": "*"&lt;/code&gt; que alguien abrió para una prueba y nunca cerró&lt;/li&gt;
&lt;li&gt;rol IAM con trust policy que confía en una cuenta externa que ya no existe como partner&lt;/li&gt;
&lt;li&gt;clave KMS accesible desde otra cuenta por una integración que ya fue migrada&lt;/li&gt;
&lt;li&gt;secret de Secrets Manager con resource policy demasiado permisiva&lt;/li&gt;
&lt;li&gt;rol con trust policy heredada hacia Cognito Identity Pools&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Estos recursos no necesariamente generan alarmas operacionales. La aplicación puede seguir funcionando normal. Nadie lo nota. El analizador los detecta porque observa la configuración de acceso, no porque espere a que ocurra un incidente.&lt;/p&gt;




&lt;h2&gt;
  
  
  Recurso recomendado para generar findings útiles
&lt;/h2&gt;

&lt;blockquote&gt;
&lt;p&gt;💡 &lt;strong&gt;Si quieres ver findings reales sin mucha configuración, este es el recurso más directo:&lt;/strong&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Crea un &lt;strong&gt;bucket S3 con una bucket policy que permita acceso desde una cuenta externa o desde &lt;code&gt;"*"&lt;/code&gt;&lt;/strong&gt;. El analizador lo detecta y genera un finding con información como recurso, principal externo, origen del acceso y nivel de acceso.&lt;/p&gt;

&lt;p&gt;Por qué S3 es un buen candidato para un lab:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Frecuente en la práctica&lt;/strong&gt;: los findings externos más comunes suelen involucrar buckets con políticas demasiado abiertas.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Finding rápido&lt;/strong&gt;: para S3, los cambios en bucket policy o ACL suelen reflejarse dentro de aproximadamente 30 minutos. Otros cambios, como Block Public Access a nivel de cuenta, pueden tardar más.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Finding detallado&lt;/strong&gt;: muestra el recurso, el principal externo, cómo se compartió y el nivel de acceso.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Fácil de limpiar&lt;/strong&gt;: eliminar o corregir la bucket policy resuelve el finding.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;En la práctica, sin embargo, activar el analizador en un entorno real con AWS IAM Identity Center ya puede generar decenas de findings sin necesidad de crear un recurso de prueba. Eso fue exactamente lo que pasó aquí.&lt;/p&gt;




&lt;h2&gt;
  
  
  External Access Analyzer en acción: lo que encuentras en un entorno real
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Configuración del analizador
&lt;/h3&gt;

&lt;p&gt;El External Access Analyzer se crea desde la consola de IAM:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;IAM &amp;gt; Access Analyzer &amp;gt; Analyzers &amp;gt; Create analyzer
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fytplfmrc8f2wuo8yu4ah.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fytplfmrc8f2wuo8yu4ah.png" alt="Menú de IAM en la consola de AWS con la opción Access Analyzer resaltada" width="244" height="655"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Configuración del lab:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Nombre:        demo-external-access-analyzer
Tipo:          External Access Analysis
Zona de trust: AWS Organization
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fjjdmvl0ibbgxm4tj3uf5.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fjjdmvl0ibbgxm4tj3uf5.png" alt="Formulario de creación de un External Access Analyzer con zona de confianza configurada como AWS Organization" width="800" height="331"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Un detalle importante: el analizador opera &lt;strong&gt;por región&lt;/strong&gt;. Si tienes recursos en &lt;code&gt;us-east-1&lt;/code&gt; y &lt;code&gt;us-east-2&lt;/code&gt;, necesitas un analizador en cada región donde quieras monitorear acceso externo. La zona de trust se define a nivel del analizador; si eliges la organización entera, los findings se calculan respecto a ese límite.&lt;/p&gt;




&lt;h3&gt;
  
  
  Lo que encontré al activarlo: 39 findings activos
&lt;/h3&gt;

&lt;p&gt;No hizo falta crear ningún recurso de prueba. En cuanto el analizador quedó activo, apareció una lista larga de findings activos.&lt;/p&gt;

&lt;p&gt;Al inicio tenía aplicado el filtro &lt;code&gt;Public access = false&lt;/code&gt;, por eso veía 38 findings. Al quitar ese filtro apareció un finding adicional: un bucket S3 con acceso público (&lt;code&gt;Public access = true&lt;/code&gt;). Por eso el total real del analizador era de 39 findings.&lt;/p&gt;

&lt;p&gt;La mayoría correspondían a roles IAM relacionados con IAM Identity Center y federación, pero también apareció un bucket S3 con acceso externo. Esa mezcla fue útil porque mostró dos tipos de exposición:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;trust policies en roles IAM&lt;/li&gt;
&lt;li&gt;resource policies en S3&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Esto es importante porque External Access Analyzer no se limita a roles. Su objetivo es detectar recursos soportados que están accesibles desde fuera de la zona de confianza definida para el analizador.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fzxfoq0dbztdcpmqqlabn.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fzxfoq0dbztdcpmqqlabn.png" alt="Lista de findings activos en External Access Analyzer con filtro Public access false aplicado" width="800" height="560"&gt;&lt;/a&gt;&lt;/p&gt;




&lt;h3&gt;
  
  
  Grupo 1 — Roles SSO: federación esperada, pero no se archiva a ciegas
&lt;/h3&gt;

&lt;p&gt;La mayoría de los findings corresponden a roles con nombres del tipo:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;aws-reserved/sso.amazonaws.com/AWSReservedSSO_AdministratorAccess_&amp;lt;sufijo&amp;gt;
aws-reserved/sso.amazonaws.com/AWSReservedSSO_AWSReadOnlyAccess_&amp;lt;sufijo&amp;gt;
aws-reserved/sso.amazonaws.com/AWSReservedSSO_AWSPowerUserAccess_&amp;lt;sufijo&amp;gt;
aws-reserved/sso.amazonaws.com/AWSReservedSSO_AWSOrganizationsFullAccess_&amp;lt;sufijo&amp;gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Estos roles los crea &lt;strong&gt;AWS IAM Identity Center&lt;/strong&gt; automáticamente en las cuentas donde asignas permission sets. Su trust policy permite que usuarios federados los asuman a través del SAML provider (en este caso yo tengo configurado cloud identity de google) asociado a IAM Identity Center:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"Effect"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Allow"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"Principal"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"Federated"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"arn:aws:iam::&amp;lt;cuenta&amp;gt;:saml-provider/AWSSSO_&amp;lt;id&amp;gt;_DO_NOT_DELETE"&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"Action"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="s2"&gt;"sts:AssumeRoleWithSAML"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="s2"&gt;"sts:TagSession"&lt;/span&gt;&lt;span class="w"&gt;
            &lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"Condition"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="nl"&gt;"StringEquals"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
            &lt;/span&gt;&lt;span class="nl"&gt;"SAML:aud"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"https://signin.aws.amazon.com/saml"&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;El analizador los marca porque, desde la perspectiva de External Access Analyzer, existe un principal federado que puede asumir el rol. En entornos con IAM Identity Center esto suele ser esperado y forma parte del funcionamiento normal de SSO.&lt;/p&gt;

&lt;p&gt;En mi caso, la revisión sería:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;confirmar que el permission set corresponde al acceso esperado&lt;/li&gt;
&lt;li&gt;confirmar que la cuenta donde aparece el rol es la correcta&lt;/li&gt;
&lt;li&gt;confirmar que los usuarios o grupos asignados siguen vigentes&lt;/li&gt;
&lt;li&gt;confirmar que los permission sets administrativos están donde realmente deben estar&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Si todo eso está correcto, estos findings son buenos candidatos para archive rule.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fw7ua42fsmiwauppluxuj.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fw7ua42fsmiwauppluxuj.png" alt="Detalle de un finding de rol SSO gestionado por IAM Identity Center con nivel de acceso Write y Tagging" width="800" height="188"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;El nivel de acceso que muestra el analizador para estos roles es &lt;code&gt;Write, Tagging&lt;/code&gt;, porque el finding está mirando la &lt;strong&gt;trust policy&lt;/strong&gt; del rol: quién puede asumirlo. No está mostrando los permisos efectivos que tendrá la sesión después de asumir el rol.&lt;/p&gt;

&lt;p&gt;Dicho de otra forma, en External Access Analyzer un finding sobre un IAM Role responde principalmente a esta pregunta:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;¿Quién puede asumir este rol desde fuera de mi zona de confianza?&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;No responde directamente:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;¿Qué puede hacer ese rol una vez asumido?&lt;/p&gt;
&lt;/blockquote&gt;




&lt;h3&gt;
  
  
  Grupo 2 — Roles con trust cross-account hacia una cuenta externa
&lt;/h3&gt;

&lt;p&gt;Cuatro findings corresponden a roles con nombres como &lt;code&gt;SSOSAdmin-Asumed&lt;/code&gt;, &lt;code&gt;SSOCores-Asumed&lt;/code&gt;, &lt;code&gt;SSOArchitech-Asumed&lt;/code&gt; y &lt;code&gt;SSOGov-Asumed&lt;/code&gt;. Todos tienen el mismo tipo de principal: una cuenta de AWS que no es parte de la organización definida como zona de trust.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Entidad principal: Cuenta de AWS → &amp;lt;cuenta-externa&amp;gt;
Compartido a través de: Trust policy del rol
Nivel de acceso: Write
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;A diferencia de los roles SSO, aquí el trust es directo hacia una cuenta AWS específica. En este contexto, “cuenta externa” no significa una cuenta desconocida, sino una cuenta fuera de la AWS Organization definida como zona de confianza. En mi caso, ese cross-account fue creado adrede desde otra organización para el lab.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2F8ypaozknhxl0hc59rlpb.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2F8ypaozknhxl0hc59rlpb.png" alt="Detalle de un finding de rol IAM con trust cross-account hacia una cuenta externa" width="800" height="279"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Aun así, el finding es útil porque recuerda algo importante: cuando la zona de trust es la organización, cualquier trust hacia una cuenta fuera de esa organización queda marcado como acceso externo.  &lt;/p&gt;

&lt;p&gt;La revisión práctica aquí no es “¿quién creó esto?”, sino:  &lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;¿este trust cross-account todavía se necesita?
&lt;/li&gt;
&lt;li&gt;¿la cuenta externa sigue siendo la correcta?
&lt;/li&gt;
&lt;li&gt;¿el rol tiene permisos acordes al caso de uso?
&lt;/li&gt;
&lt;li&gt;¿conviene agregar condiciones como &lt;code&gt;sts:ExternalId&lt;/code&gt;, &lt;code&gt;aws:PrincipalArn&lt;/code&gt; u otra restricción?
&lt;/li&gt;
&lt;li&gt;¿está documentado que esta cuenta externa puede asumir el rol?
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Importante: aunque el rol tenga &lt;code&gt;AdministratorAccess&lt;/code&gt;, el finding no muestra &lt;code&gt;AdministratorAccess&lt;/code&gt; como acción. External Access Analyzer está evaluando la trust policy del rol, por eso muestra &lt;code&gt;sts:AssumeRole&lt;/code&gt;.  &lt;/p&gt;

&lt;p&gt;El finding responde:  &lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;¿quién puede asumir este rol desde fuera de mi zona de confianza?  &lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;No responde directamente:  &lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;¿qué permisos tendrá esa sesión después de asumirlo?  &lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Para medir el impacto real, hay que revisar también las policies adjuntas al rol. Si el rol confiado por una cuenta externa tiene &lt;code&gt;AdministratorAccess&lt;/code&gt;, entonces el finding es más sensible, aunque en la consola solo aparezca &lt;code&gt;sts:AssumeRole&lt;/code&gt;.&lt;/p&gt;




&lt;h3&gt;
  
  
  Grupo 3 — Federación con Google Workspace: SAML externo personalizado
&lt;/h3&gt;

&lt;p&gt;Un finding apunta al rol &lt;code&gt;SSOTestSamlWorkspace&lt;/code&gt;, con trust configurado hacia un SAML provider &lt;code&gt;GoogleWorkspace&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Entidad principal: Usuario federado → arn:aws:iam::&amp;lt;cuenta&amp;gt;:saml-provider/GoogleWorkspace
Compartido a través de: Trust policy del rol
Nivel de acceso: Write
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Este caso es diferente a los roles &lt;code&gt;AWSReservedSSO_*&lt;/code&gt; gestionados por IAM Identity Center. Aquí la confianza está puesta directamente en un SAML provider personalizado.&lt;/p&gt;

&lt;p&gt;En mi caso, este rol existe porque uso Google Workspace / Cloud Identity como parte del flujo de federación para entrar a AWS. El analyzer no está diciendo “esto está mal”; está diciendo “este proveedor federado puede asumir este rol”.&lt;/p&gt;

&lt;p&gt;Y eso es justo lo valioso: aunque uno mismo lo haya creado, el analyzer lo pone sobre la mesa para decidir si sigue siendo necesario, si debe documentarse mejor, si debe restringirse más o si ya toca borrarlo.&lt;/p&gt;

&lt;p&gt;Dicho en simple: gracias, analyzer, por recordarme que este camino de acceso sigue vivo.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fd6t9vfz7btuqnro6uglk.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fd6t9vfz7btuqnro6uglk.png" alt="Detalle de un finding de federación SAML con proveedor GoogleWorkspace" width="800" height="269"&gt;&lt;/a&gt;&lt;/p&gt;




&lt;h3&gt;
  
  
  Grupo 4 — Cognito: un rol que quedó vivo después de una prueba
&lt;/h3&gt;

&lt;p&gt;Entre todos los findings, hay uno que tiene una naturaleza diferente al resto:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Recurso:    service-role/cognito-testing
Principal:  cognito-identity.amazonaws.com
Condición:  us-east-1:&amp;lt;id-del-pool&amp;gt;  ← pool de Cognito
Nivel:      Write
Tipo:       Público de Cognito
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fwifi5do9r0cyeumzt6ye.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fwifi5do9r0cyeumzt6ye.png" alt="Detalle de un finding de rol IAM con trust policy hacia Cognito Identity" width="799" height="276"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Este finding no venía de un Cognito User Pool, sino de una trust policy de IAM asociada a Cognito Identity Pools. La diferencia importa: el User Pool autentica usuarios, mientras que el Identity Pool puede entregar credenciales temporales de AWS mediante roles IAM.&lt;/p&gt;

&lt;p&gt;En mi caso, esto venía de una prueba antigua. En algún momento creé un Identity Pool, luego lo eliminé, pero el rol &lt;code&gt;cognito-testing&lt;/code&gt; quedó vivo con esta trust policy:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"Effect"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Allow"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"Principal"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"Federated"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"cognito-identity.amazonaws.com"&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"Action"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"sts:AssumeRoleWithWebIdentity"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"Condition"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"StringEquals"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"cognito-identity.amazonaws.com:aud"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"&amp;lt;id-del-pool&amp;gt;"&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"ForAnyValue:StringLike"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"cognito-identity.amazonaws.com:amr"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"unauthenticated"&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;La conclusión no es que actualmente exista un Identity Pool público activo, porque al revisar la cuenta ya no había Identity Pools en esa región. La conclusión correcta es que quedó un rol IAM con una trust policy heredada de una prueba anterior.&lt;/p&gt;

&lt;p&gt;Y justamente ahí está el valor del analyzer: no solo encuentra exposiciones activas evidentes, también te recuerda configuraciones viejas que quedaron dando vueltas.&lt;/p&gt;

&lt;p&gt;Gracias, analyzer. Me olvidé de borrar el rol.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fw4035d5bzkw2ogrizb73.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fw4035d5bzkw2ogrizb73.png" alt="Validación del rol cognito-testing con trust policy heredada hacia Cognito Identity Pools" width="372" height="224"&gt;&lt;/a&gt;&lt;/p&gt;




&lt;h3&gt;
  
  
  Grupo 5 — Bucket S3 con acceso público
&lt;/h3&gt;

&lt;p&gt;Al quitar el filtro &lt;code&gt;Public access = false&lt;/code&gt;, apareció un finding adicional sobre un bucket S3:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Recurso: demo-api-gw-s3-cors-555
Tipo:    S3 Bucket
Principal externo: Todas las entidades principales
Acceso público: true
Nivel de acceso: Write, Permissions, Tagging, Read, List
Compartido a través de: Política del bucket
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Este bucket fue parte de una prueba de API Gateway + S3/CORS. Justamente por eso el finding es útil: no necesariamente es un recurso productivo comprometido, pero sí es una exposición real que quedó visible para revisión.&lt;/p&gt;

&lt;p&gt;A diferencia de los roles SSO, aquí no hablamos de una trust policy de IAM. Hablamos de una bucket policy que permite acceso a todas las entidades principales.&lt;/p&gt;

&lt;p&gt;Este finding merece remediación prioritaria porque combina dos señales fuertes:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;el principal externo es público&lt;/li&gt;
&lt;li&gt;el nivel de acceso incluye más que lectura&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;En mi caso, como era una prueba, la decisión correcta es simple: corregir la bucket policy, activar/verificar Block Public Access o eliminar el bucket si ya no se necesita.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2F7e32szop2ycx1zu5yhlk.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2F7e32szop2ycx1zu5yhlk.png" alt="Detalle de un finding de bucket S3 público con principal Todas las entidades principales" width="800" height="518"&gt;&lt;/a&gt;&lt;/p&gt;




&lt;h3&gt;
  
  
  Categorización práctica de los findings
&lt;/h3&gt;

&lt;p&gt;Con este volumen de findings, la primera tarea es categorizarlos para saber qué revisar primero:&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Categoría&lt;/th&gt;
&lt;th&gt;Ejemplo&lt;/th&gt;
&lt;th&gt;Acción recomendada&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Roles SSO gestionados por AWS&lt;/td&gt;
&lt;td&gt;
&lt;code&gt;AWSReservedSSO_*&lt;/code&gt; con SAML provider &lt;code&gt;DO_NOT_DELETE&lt;/code&gt;
&lt;/td&gt;
&lt;td&gt;Validar permission set, cuenta y asignaciones; luego archivar con archive rule si es esperado&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Roles custom con trust cross-account&lt;/td&gt;
&lt;td&gt;
&lt;code&gt;SSOSAdmin-Asumed&lt;/code&gt; confiando en cuenta externa&lt;/td&gt;
&lt;td&gt;Revisar si el acceso sigue vigente, documentado y justificado&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Roles con SAML provider externo personalizado&lt;/td&gt;
&lt;td&gt;
&lt;code&gt;SSOTestSamlWorkspace&lt;/code&gt; con GoogleWorkspace&lt;/td&gt;
&lt;td&gt;Revisar si el provider y el rol siguen activos&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Rol con trust heredada hacia Cognito&lt;/td&gt;
&lt;td&gt;
&lt;code&gt;cognito-testing&lt;/code&gt; con &lt;code&gt;amr = unauthenticated&lt;/code&gt; en la trust policy&lt;/td&gt;
&lt;td&gt;Validar si el Identity Pool sigue existiendo; si no existe, eliminar o limpiar el rol&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Bucket S3 público&lt;/td&gt;
&lt;td&gt;
&lt;code&gt;demo-api-gw-s3-cors-555&lt;/code&gt; con principal público&lt;/td&gt;
&lt;td&gt;Revisar bucket policy, Block Public Access y necesidad real de exposición pública&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;El orden de prioridad no depende solo del volumen. Los roles SSO pueden ser muchos, pero suelen ser esperados si IAM Identity Center está bien gobernado. En cambio, un bucket S3 público con permisos amplios o un rol con una trust policy heredada hacia Cognito pueden representar una superficie de exposición mucho más concreta.&lt;/p&gt;




&lt;h3&gt;
  
  
  Archive Rules: reducir el ruido esperado
&lt;/h3&gt;

&lt;p&gt;Con decenas de findings de roles SSO esperados, crear una &lt;strong&gt;Archive Rule&lt;/strong&gt; es lo más eficiente. Las archive rules sirven para archivar automáticamente nuevos findings que coincidan con un patrón. Para findings existentes, puedes revisarlos y archivarlos manualmente, o aplicar la regla cuando corresponda desde el flujo de revisión.&lt;/p&gt;

&lt;p&gt;Se crean desde:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;IAM &amp;gt; Access Analyzer &amp;gt; Analyzers &amp;gt; [tu analizador] &amp;gt; Archive rules &amp;gt; Create rule
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Frazu0cgwwghe3lukflv5.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Frazu0cgwwghe3lukflv5.png" alt="Pantalla de creación de archive rule para archivar findings esperados de roles SSO" width="800" height="533"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Para los roles SSO, una condición útil es filtrar por el nombre del recurso:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Resource contains "aws-reserved/sso.amazonaws.com/AWSReservedSSO_"
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Eso ayuda a archivar automáticamente los nuevos findings asociados a roles gestionados por IAM Identity Center, sin mezclar findings más sensibles como buckets públicos, roles custom con trust cross-account, SAML providers externos personalizados o Cognito.&lt;/p&gt;

&lt;p&gt;Las condiciones disponibles para los filtros dependen de la consola/API, pero normalmente puedes filtrar por campos como:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Resource&lt;/strong&gt;&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Principal&lt;/strong&gt;&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Access level&lt;/strong&gt;&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Is public&lt;/strong&gt;&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Principal type&lt;/strong&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Importante: una archive rule no debe usarse para ocultar findings sin revisión. Primero se valida que el patrón sea esperado; recién después se automatiza el archivado.&lt;/p&gt;




&lt;h3&gt;
  
  
  Recursos soportados por External Access Analyzer
&lt;/h3&gt;

&lt;p&gt;El analizador no solo cubre roles IAM. Estos son los tipos de recursos que puede analizar para external access:&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Recurso&lt;/th&gt;
&lt;th&gt;Qué evalúa&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;S3 Buckets&lt;/td&gt;
&lt;td&gt;Bucket policy, ACLs, access points asociados y Multi-Region Access Points&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;S3 Directory Buckets&lt;/td&gt;
&lt;td&gt;Directory bucket policy&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;IAM Roles&lt;/td&gt;
&lt;td&gt;Trust policy&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;KMS Keys&lt;/td&gt;
&lt;td&gt;Key policy y grants&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Lambda Functions and Layers&lt;/td&gt;
&lt;td&gt;Resource-based policies&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;SQS Queues&lt;/td&gt;
&lt;td&gt;Queue policy&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;SNS Topics&lt;/td&gt;
&lt;td&gt;Topic policy&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Secrets Manager&lt;/td&gt;
&lt;td&gt;Resource policy&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;ECR Repositories&lt;/td&gt;
&lt;td&gt;Repository policy&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;EFS File Systems&lt;/td&gt;
&lt;td&gt;File system policy&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;EBS Volume Snapshots&lt;/td&gt;
&lt;td&gt;Snapshot sharing permissions&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;RDS DB Snapshots&lt;/td&gt;
&lt;td&gt;Snapshot sharing permissions&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;RDS DB Cluster Snapshots&lt;/td&gt;
&lt;td&gt;Snapshot sharing permissions&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;DynamoDB Streams&lt;/td&gt;
&lt;td&gt;Resource policy&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;DynamoDB Tables&lt;/td&gt;
&lt;td&gt;Resource policy&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;En este lab, la mayoría de findings fueron sobre roles IAM porque el entorno tenía muchos roles SSO y roles con trust policies hacia identidades externas. Sin embargo, también apareció un bucket S3 público. En entornos donde existan claves KMS compartidas, secretos con resource policies externas, colas SQS compartidas o snapshots públicos, el analizador también puede generar findings de esos tipos.&lt;/p&gt;




&lt;h3&gt;
  
  
  Qué hacer con un finding: Archive vs. remediar
&lt;/h3&gt;

&lt;p&gt;Cuando un finding aparece, tienes dos caminos:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;1. Si el acceso es intencional:&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Haz clic en &lt;strong&gt;Archive&lt;/strong&gt;. El finding pasa a estado &lt;code&gt;Archived&lt;/code&gt; y queda fuera de la vista activa. También puedes crear archive rules para que findings similares se archiven automáticamente después de haber validado que ese patrón es esperado.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;2. Si el acceso no es intencional:&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Modifica o elimina la política o configuración que abre el acceso. En cuanto el recurso deja de tener acceso externo, el finding pasa a estado &lt;code&gt;Resolved&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;Archivar no es remediar. Archivar significa: “este acceso fue revisado y es intencional”. Si el acceso no debería existir, lo correcto no es archivarlo; lo correcto es corregir la policy.&lt;/p&gt;




&lt;h2&gt;
  
  
  External Access vs. Internal Access: cuándo usar cada uno
&lt;/h2&gt;

&lt;p&gt;Antes de pasar al siguiente post, vale la pena separar ambos conceptos.&lt;/p&gt;

&lt;p&gt;External Access Analyzer e Internal Access Analyzer pertenecen a IAM Access Analyzer, pero responden preguntas distintas. External mira hacia fuera: recursos accesibles desde fuera de la zona de confianza. Internal mira hacia dentro: identidades internas que tienen acceso a recursos seleccionados.&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Criterio&lt;/th&gt;
&lt;th&gt;External Access Analyzer&lt;/th&gt;
&lt;th&gt;Internal Access Analyzer&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Pregunta que responde&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;¿Qué recursos están accesibles desde fuera de mi zona de confianza?&lt;/td&gt;
&lt;td&gt;¿Qué identidades internas tienen acceso a recursos críticos seleccionados?&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Zona de confianza&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Cuenta o AWS Organization&lt;/td&gt;
&lt;td&gt;Cuenta o AWS Organization&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Tipo de exposición&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Acceso público o cross-account externo&lt;/td&gt;
&lt;td&gt;Acceso interno dentro de la cuenta u organización&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Qué analiza principalmente&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Resource-based policies, trust policies, ACLs o grants según el tipo de recurso&lt;/td&gt;
&lt;td&gt;Permisos efectivos sobre recursos específicos seleccionados&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Ejemplos de recursos&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Roles IAM, buckets S3, KMS keys, Lambda, SQS, SNS, Secrets Manager, ECR, EFS, snapshots, DynamoDB, entre otros recursos soportados&lt;/td&gt;
&lt;td&gt;Recursos específicos que seleccionas para análisis interno&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Cobertura&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Recursos soportados en la región donde el analyzer está activo&lt;/td&gt;
&lt;td&gt;Solo los recursos que seleccionas explícitamente&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Costo&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Sin costo adicional&lt;/td&gt;
&lt;td&gt;Pagado por recurso monitoreado, por analyzer y por región&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;SCP/RCP&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;No es su objetivo principal como cálculo de permiso efectivo; se enfoca en exposición externa desde políticas de recurso, trust policies, ACLs o grants&lt;/td&gt;
&lt;td&gt;Sí entra en el modelo de análisis de permiso efectivo, aunque la interpretación del action list requiere cuidado&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Mejor uso&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Baseline permanente para detectar exposición pública o externa&lt;/td&gt;
&lt;td&gt;Auditoría específica de acceso interno sobre recursos sensibles&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Recomendación práctica&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Activarlo en todas las cuentas y regiones relevantes&lt;/td&gt;
&lt;td&gt;Usarlo con criterio sobre recursos críticos por costo y alcance&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;La forma corta de verlo es esta:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;External Access Analyzer&lt;/strong&gt; sirve para detectar exposición hacia fuera.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Internal Access Analyzer&lt;/strong&gt; sirve para revisar acceso interno sobre recursos críticos.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;External&lt;/strong&gt; debería ser baseline.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Internal&lt;/strong&gt; debería usarse selectivamente donde realmente necesitas análisis de acceso efectivo.&lt;/li&gt;
&lt;/ul&gt;




&lt;h2&gt;
  
  
  Perspectiva de seguridad: por qué los findings deben revisarse antes de archivarse
&lt;/h2&gt;

&lt;p&gt;External Access Analyzer también es útil desde una perspectiva de evaluación de seguridad.&lt;/p&gt;

&lt;p&gt;Un usuario o rol con permisos suficientes para consultar Access Analyzer podría usar los findings como un mapa rápido de recursos expuestos externamente: buckets con acceso público, roles con trust policies hacia cuentas externas, proveedores SAML externos o configuraciones de Cognito que permitan identidades no autenticadas.&lt;/p&gt;

&lt;p&gt;Eso no significa que cualquier finding sea automáticamente crítico. Significa que cada finding representa una ruta de acceso que existe y que merece clasificación.&lt;/p&gt;

&lt;p&gt;Desde el lado defensivo, la prioridad es llegar antes:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Activar el analyzer.&lt;/li&gt;
&lt;li&gt;Revisar los findings.&lt;/li&gt;
&lt;li&gt;Separar accesos esperados de accesos no documentados.&lt;/li&gt;
&lt;li&gt;Archivar lo intencional.&lt;/li&gt;
&lt;li&gt;Remediar lo que no debería existir.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Los findings sin revisar son el problema real. Un bucket público intencional, documentado y monitoreado no tiene el mismo peso que un bucket público creado para una prueba y olvidado. Lo mismo aplica para roles federados, trusts cross-account o pools de Cognito.&lt;/p&gt;

&lt;p&gt;En este lab, la mayoría de findings eran esperados por IAM Identity Center, pero el bucket S3 público y el rol con trust heredada hacia Cognito merecían revisión separada. Esa es precisamente la utilidad del analyzer: no reemplaza el criterio del equipo de seguridad, pero reduce mucho el tiempo necesario para encontrar qué revisar primero.&lt;/p&gt;




&lt;h2&gt;
  
  
  Lo que demuestra este lab
&lt;/h2&gt;

&lt;p&gt;Activar el analizador en un entorno real con IAM Identity Center puede generar hallazgos desde los primeros minutos, sin necesidad de crear recursos de prueba. Eso ya dice algo: hay rutas de acceso que merecen revisión y que no siempre son visibles si solo revisas manualmente cada cuenta o cada policy.&lt;/p&gt;

&lt;p&gt;Lo que vale de este analizador no es que reemplace el criterio del equipo de seguridad. Su valor está en que es &lt;strong&gt;continuo y automático&lt;/strong&gt;: revisa los recursos soportados y genera findings cuando detecta acceso público o externo respecto a tu zona de confianza.&lt;/p&gt;

&lt;p&gt;En términos prácticos:&lt;/p&gt;

&lt;p&gt;✅ Sin costo adicional&lt;br&gt;
✅ Cobertura automática de recursos soportados en la región donde se habilita&lt;br&gt;
✅ Findings con detalle de recurso, principal, tipo de acceso y acciones reportadas&lt;br&gt;
✅ Archive rules para reducir ruido esperado, como roles SSO gestionados por IAM Identity Center&lt;br&gt;
✅ Estado &lt;code&gt;Resolved&lt;/code&gt; cuando el acceso externo deja de existir&lt;/p&gt;

&lt;p&gt;Y la lección más importante del lab: el volumen de findings no es el indicador principal de riesgo.&lt;/p&gt;

&lt;p&gt;En mi caso, con el filtro &lt;code&gt;Public access = false&lt;/code&gt; veía 38 findings. Al quitarlo, apareció un bucket S3 público y el total subió a 39. La mayoría eran roles SSO esperados, pero el bucket S3 público y el rol con trust heredada hacia Cognito merecían revisión separada. Ese es precisamente el punto: el analyzer no solo te muestra cuántos findings tienes, sino cuáles debes clasificar primero.&lt;/p&gt;




&lt;h2&gt;
  
  
  Conclusión
&lt;/h2&gt;

&lt;p&gt;Si tienes una cuenta de AWS y no tienes External Access Analyzer activo, es uno de los primeros controles que habilitaría. No tiene costo adicional, se activa en minutos y te da visibilidad sobre accesos que normalmente no generan una alerta operacional por sí solos.&lt;/p&gt;

&lt;p&gt;Lo que pasó en este lab es representativo de muchos entornos con IAM Identity Center: aparece un volumen alto de findings esperados, especialmente por roles SSO, pero entre ellos puede haber uno o dos findings que sí merecen atención inmediata.&lt;/p&gt;

&lt;p&gt;La habilidad no está solo en activar el analyzer. La habilidad está en clasificar rápido:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;qué es esperado&lt;/li&gt;
&lt;li&gt;qué debe archivarse&lt;/li&gt;
&lt;li&gt;qué debe documentarse&lt;/li&gt;
&lt;li&gt;qué debe corregirse&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;En el siguiente post cubro &lt;strong&gt;Internal Access Analyzer&lt;/strong&gt;: cómo analizar acceso interno efectivo sobre un recurso crítico, cómo se cruzan IAM policies, bucket policies y SCPs, y por qué no basta con revisar una sola policy.&lt;/p&gt;




&lt;p&gt;💡 &lt;strong&gt;¿Cuántos findings aparecen cuando activas el analizador por primera vez en tu entorno?&lt;/strong&gt; El número suele ser una sorpresa — y la mayoría son esperados, pero entre ellos casi siempre hay algo que merece atención.&lt;/p&gt;

&lt;p&gt;🔗 Si llegaste directo aquí, te recomiendo leer la Parte 1 para el contexto base. En la Parte 3 veremos Internal Access Analyzer con un lab de permisos efectivos, SCPs y acceso cross-account interno.&lt;/p&gt;

&lt;h1&gt;
  
  
  AWS #CloudSecurity #IAM #AccessAnalyzer #IAMIdentityCenter #SSO #CognitoSecurity #ExternalAccess #CloudGovernance #AWSOrganizations #LeastPrivilege
&lt;/h1&gt;




&lt;h2&gt;
  
  
  Referencias
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;AWS IAM Documentation — &lt;strong&gt;What is IAM Access Analyzer?&lt;/strong&gt;&lt;br&gt;
&lt;a href="https://docs.aws.amazon.com/IAM/latest/UserGuide/what-is-access-analyzer.html" rel="noopener noreferrer"&gt;https://docs.aws.amazon.com/IAM/latest/UserGuide/what-is-access-analyzer.html&lt;/a&gt;&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;AWS IAM Documentation — &lt;strong&gt;IAM Access Analyzer supported resource types for external and internal access&lt;/strong&gt;&lt;br&gt;
&lt;a href="https://docs.aws.amazon.com/IAM/latest/UserGuide/access-analyzer-resources.html" rel="noopener noreferrer"&gt;https://docs.aws.amazon.com/IAM/latest/UserGuide/access-analyzer-resources.html&lt;/a&gt;&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;AWS IAM Documentation — &lt;strong&gt;Create an IAM Access Analyzer external access analyzer&lt;/strong&gt;&lt;br&gt;
&lt;a href="https://docs.aws.amazon.com/IAM/latest/UserGuide/access-analyzer-create-external.html" rel="noopener noreferrer"&gt;https://docs.aws.amazon.com/IAM/latest/UserGuide/access-analyzer-create-external.html&lt;/a&gt;&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;AWS IAM Documentation — &lt;strong&gt;IAM Access Analyzer findings&lt;/strong&gt;&lt;br&gt;
&lt;a href="https://docs.aws.amazon.com/IAM/latest/UserGuide/access-analyzer-findings.html" rel="noopener noreferrer"&gt;https://docs.aws.amazon.com/IAM/latest/UserGuide/access-analyzer-findings.html&lt;/a&gt;&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;AWS IAM Documentation — &lt;strong&gt;Archive rules&lt;/strong&gt;&lt;br&gt;
&lt;a href="https://docs.aws.amazon.com/IAM/latest/UserGuide/access-analyzer-archive-rules.html" rel="noopener noreferrer"&gt;https://docs.aws.amazon.com/IAM/latest/UserGuide/access-analyzer-archive-rules.html&lt;/a&gt;&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;AWS Pricing — &lt;strong&gt;IAM Access Analyzer pricing&lt;/strong&gt;&lt;br&gt;
&lt;a href="https://aws.amazon.com/iam/access-analyzer/pricing/" rel="noopener noreferrer"&gt;https://aws.amazon.com/iam/access-analyzer/pricing/&lt;/a&gt;&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;AWS S3 Documentation — &lt;strong&gt;Reviewing bucket access using IAM Access Analyzer for S3&lt;/strong&gt;&lt;br&gt;
&lt;a href="https://docs.aws.amazon.com/AmazonS3/latest/userguide/access-analyzer.html" rel="noopener noreferrer"&gt;https://docs.aws.amazon.com/AmazonS3/latest/userguide/access-analyzer.html&lt;/a&gt;&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;AWS S3 Documentation — &lt;strong&gt;Blocking public access to your Amazon S3 storage&lt;/strong&gt;&lt;br&gt;
&lt;a href="https://docs.aws.amazon.com/AmazonS3/latest/userguide/access-control-block-public-access.html" rel="noopener noreferrer"&gt;https://docs.aws.amazon.com/AmazonS3/latest/userguide/access-control-block-public-access.html&lt;/a&gt;&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

</description>
      <category>aws</category>
      <category>iam</category>
      <category>tutorial</category>
      <category>cybersecurity</category>
    </item>
    <item>
      <title>🛡️AWS IAM Access Analyzer — Parte 1/4</title>
      <dc:creator>Terry Quispe Paniagua</dc:creator>
      <pubDate>Sun, 21 Jun 2026 01:14:43 +0000</pubDate>
      <link>https://dev.to/terry_cloud/aws-iam-access-analyzer-parte-14-22io</link>
      <guid>https://dev.to/terry_cloud/aws-iam-access-analyzer-parte-14-22io</guid>
      <description>&lt;h2&gt;
  
  
  &lt;strong&gt;¿Quién puede acceder realmente a tus recursos en AWS?&lt;/strong&gt;
&lt;/h2&gt;

&lt;p&gt;En el post anterior hablamos de SCPs, RCPs y IAM Policies. Si no lo leíste, te recomiendo empezar por ahí porque este post construye sobre eso.&lt;/p&gt;

&lt;p&gt;Hoy quiero hacerte una pregunta incómoda:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;"¿Puedes decirme, ahora mismo, qué identidades tienen acceso efectivo a este bucket S3?"&lt;/strong&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;No el acceso que &lt;em&gt;pensaste&lt;/em&gt; que configuraste. El acceso &lt;strong&gt;real&lt;/strong&gt;, considerando todas las políticas en juego.&lt;/p&gt;

&lt;p&gt;Si tardas más de 30 segundos en responder, este post es para ti.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2F4ac1j0nbbc957kae7ree.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2F4ac1j0nbbc957kae7ree.png" alt=" " width="800" height="450"&gt;&lt;/a&gt;&lt;/p&gt;




&lt;h2&gt;
  
  
  &lt;strong&gt;El problema con el acceso en AWS&lt;/strong&gt;
&lt;/h2&gt;

&lt;p&gt;Cuando tienes una sola cuenta y cuatro usuarios, responder esa pregunta es fácil. Abres la consola, revisas las políticas y listo.&lt;/p&gt;

&lt;p&gt;Pero hay una diferencia importante entre dos cosas que suelen confundirse:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Acceso esperado&lt;/strong&gt;: lo que crees que configuraste, suponiendo que sea correcto.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Acceso efectivo&lt;/strong&gt;: lo que AWS realmente permite después de evaluar todas las políticas&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;La brecha entre estas dos cosas es donde vive la mayoría de los incidentes de seguridad en AWS. No son ataques sofisticados. Son configuraciones que alguien dio por buenas porque "la IAM policy dice que sí" — sin considerar el resto de las capas.&lt;/p&gt;

&lt;p&gt;En el mundo real, un entorno AWS medianamente serio se parece más a esto:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;10, 20, 50 o más cuentas dentro de una AWS Organization&lt;/li&gt;
&lt;li&gt;Decenas de roles que se asumen entre cuentas - en algunas ocasiones he visto miles -.&lt;/li&gt;
&lt;li&gt;SCPs aplicadas a nivel de OU que limitan lo que pueden hacer las identidades.&lt;/li&gt;
&lt;li&gt;RCPs que controlan quién puede acceder a tus recursos desde fuera.&lt;/li&gt;
&lt;li&gt;Resource-based policies en S3, KMS, SQS, SNS, etc.&lt;/li&gt;
&lt;li&gt;Permission Boundaries en algunos roles.&lt;/li&gt;
&lt;li&gt;Trust policies que definen quién puede asumir cada rol.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Y ahí es donde la cosa se complica de verdad.&lt;/p&gt;




&lt;h2&gt;
  
  
  &lt;strong&gt;¿Qué determina realmente el acceso efectivo?&lt;/strong&gt;
&lt;/h2&gt;

&lt;p&gt;Cuando AWS evalúa si una acción está permitida, no se queda solo con la IAM policy que ves en el rol o usuario. Revisa todas las capas que aplican a esa solicitud y parte de una regla muy simple: si no hay un &lt;code&gt;Allow&lt;/code&gt;, el acceso está denegado; y si aparece un &lt;code&gt;Deny&lt;/code&gt; explícito, ese &lt;code&gt;Deny&lt;/code&gt; gana siempre.&lt;/p&gt;

&lt;p&gt;Dicho de forma simple, en una sola cuenta la evaluación se puede entender así:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;¿Existe un Deny explícito aplicable?
    └── Sí → DENEGADO. Fin.
    └── No → continuar

¿Existe algún Allow válido?
    └── Puede venir de una IAM policy o de una resource-based policy
    └── Si no existe → DENEGADO

¿Hay algún límite que recorte ese permiso?
    └── SCP
    └── RCP
    └── Permission Boundary
    └── Session Policy

Si hay Allow y ningún límite lo bloquea → PERMITIDO
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;La parte importante es esta: IAM policies y resource-based policies pueden conceder permisos, pero SCPs, RCPs, Permission Boundaries y Session Policies funcionan más como límites. No agregan permisos por sí solos; más bien definen hasta dónde puede llegar un permiso que ya existe.&lt;/p&gt;

&lt;p&gt;Resumido de forma práctica:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Acceso efectivo =
    permiso concedido
  - denies explícitos
  - límites organizacionales
  - boundaries o session policies aplicables
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Si &lt;strong&gt;cualquiera&lt;/strong&gt; de estas capas dice "No", la acción está bloqueada. No importa que las demás capas parezcan permitirlo. Este es el punto que la gente entiende en teoría pero subestima en la práctica. En cross-account hay matices adicionales, porque AWS evalúa tanto la cuenta del principal como la cuenta donde vive el recurso. Pero la idea base se mantiene: cualquier deny explícito aplicable bloquea la acción. Puedes revisar el flujo de evaluación en:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;&lt;a href="//reference_policies_evaluation-logic_policy-eval-basics.html"&gt;Single account&lt;/a&gt;&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;a href="//reference_policies_evaluation-logic-cross-account.html"&gt;cross-account&lt;/a&gt;&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;a href="//reference_policies_evaluation-logic.html"&gt;Policy evaluation logic general&lt;/a&gt;&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;El problema a escala: con múltiples cuentas, cientos o miles de roles, innumerables recursos, varias OUs con diferentes SCPs y RCPs, hacer este análisis a mano para cada recurso crítico es inviable. Y si no lo haces, no sabes realmente qué acceso tienen tus identidades.&lt;/p&gt;




&lt;h2&gt;
  
  
  &lt;strong&gt;Tres escenarios donde el acceso falla aunque "debería" funcionar&lt;/strong&gt;
&lt;/h2&gt;

&lt;p&gt;Estos son los casos que más se repiten. Si has trabajado en entornos con Organizations, probablemente reconoces alguno.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Escenario 1: El admin que no puede hacer nada&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Un usuario tiene la política &lt;code&gt;AdministratorAccess&lt;/code&gt; en su cuenta. Intenta crear un bucket S3 en &lt;code&gt;sa-east-1&lt;/code&gt; y le da error de acceso denegado. No cambió nada. Todo funcionaba ayer.&lt;/p&gt;

&lt;p&gt;Lo que pasó: alguien en el equipo de plataforma aplicó una SCP a nivel de OU que restringe la creación de recursos a &lt;code&gt;us-east-1&lt;/code&gt; únicamente - algo muy habitual y recomendado -. El admin ve un &lt;code&gt;AccessDenied&lt;/code&gt;, pero también el motivo por el cual, ya que en el detalle de error se indica que la acción está vetada por una SCP - a veces no es tan detallado -.&lt;/p&gt;

&lt;p&gt;La SCP es un límite máximo. &lt;code&gt;AdministratorAccess&lt;/code&gt; dice "puede hacer todo", pero si la SCP dice "solo en us-east-1", el resultado es "puede hacer todo, solo en us-east-1".&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Ejemplo: Eliminación de objeto con restricción por SCP&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fhppdpxf4tfssi89325ls.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fhppdpxf4tfssi89325ls.png" alt=" " width="799" height="152"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Ejemplo: Eliminación de objeto por CLI
&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fmdswgz25d0y7qnodqzw1.png" alt=" " width="796" height="60"&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Escenario 2: El bucket "público" que no lo es&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Un desarrollador configura un bucket S3 con una bucket policy que permite &lt;code&gt;s3:GetObject&lt;/code&gt; a &lt;code&gt;"Principal": "*"&lt;/code&gt;. Desde su cuenta local, el acceso funciona. Pero desde una cuenta externa de un proveedor, el acceso falla.&lt;/p&gt;

&lt;p&gt;Lo que pasó: hay una RCP a nivel de organización que exige que cualquier acceso a recursos de S3 venga de un principal con &lt;code&gt;aws:PrincipalOrgID&lt;/code&gt; igual al ID de la organización. El &lt;code&gt;"*"&lt;/code&gt; de la bucket policy no se limita a sí mismo — la RCP sí lo limita desde afuera.&lt;/p&gt;

&lt;p&gt;La RCP actúa sobre el recurso sin importar lo que diga la bucket policy. El desarrollador configuró el acceso "correctamente" según lo que entiende de S3, pero la capa organizacional lo restringe silenciosamente.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Escenario 3: El rol cross-account que IAM permite pero S3 no&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Un rol en la Cuenta B tiene una IAM policy que permite &lt;code&gt;s3:GetObject&lt;/code&gt; sobre el bucket &lt;code&gt;arn:aws:s3:::datos-criticos/*&lt;/code&gt;. El equipo valida la policy, todo se ve bien. Pero cuando el rol intenta acceder al bucket, falla.&lt;/p&gt;

&lt;p&gt;Lo que pasó: la bucket policy en la Cuenta A (donde vive el bucket) no lista el rol de Cuenta B como Principal permitido. En acceso cross-account, tanto la IAM policy del rol como la resource-based policy del recurso deben permitir la acción de forma explícita. Una sola no alcanza.&lt;/p&gt;

&lt;p&gt;Este es uno de los errores más comunes con acceso cross-account: el equipo de Cuenta B configura sus políticas IAM perfectamente y asume que eso es suficiente. No lo es.&lt;/p&gt;




&lt;h2&gt;
  
  
  &lt;strong&gt;¿Qué es IAM Access Analyzer y qué tipos existen?&lt;/strong&gt;
&lt;/h2&gt;

&lt;p&gt;IAM Access Analyzer es el servicio de AWS que ayuda a identificar y revisar accesos sobre recursos e identidades. En vez de revisar políticas manualmente y cruzar capas en tu cabeza — lo que puede ser propenso a errores y muy complicado — , el analizador lo hace por ti y genera &lt;em&gt;findings&lt;/em&gt; — hallazgos sobre accesos que merecen revisión.&lt;/p&gt;

&lt;p&gt;Hay tres tipos de analizador, y es importante no confundirlos porque resuelven problemas distintos:&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Tipo&lt;/th&gt;
&lt;th&gt;¿Qué detecta?&lt;/th&gt;
&lt;th&gt;¿Tiene costo?&lt;/th&gt;
&lt;th&gt;¿Cuándo usarlo?&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;External Access&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Recursos accesibles desde fuera de tu zona de confianza (otra cuenta, internet)&lt;/td&gt;
&lt;td&gt;Sin costo adicional&lt;/td&gt;
&lt;td&gt;Siempre. Es el punto de partida en cualquier cuenta&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Internal Access&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Qué principals &lt;em&gt;dentro&lt;/em&gt; de tu org tienen acceso a recursos específicos, con permisos efectivos&lt;/td&gt;
&lt;td&gt;Pagado ($9 USD/recurso/región/mes)&lt;/td&gt;
&lt;td&gt;Recursos críticos: buckets, RDS o cluster snapshots, DynamoDB etc.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Unused Access&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Roles y usuarios con permisos concedidos que no han sido utilizados&lt;/td&gt;
&lt;td&gt;Pagado ($0.20 USD/rol o usuario/mes)&lt;/td&gt;
&lt;td&gt;Revisiones de least privilege, limpieza de accesos obsoletos&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;La confusión más común es querer usar &lt;strong&gt;Internal Access&lt;/strong&gt; para detectar accesos externos. No hace eso. Para eso existe &lt;strong&gt;External Access&lt;/strong&gt;, que además es gratuito.&lt;/p&gt;

&lt;p&gt;La novedad relevante que cubre esta serie es &lt;strong&gt;Internal Access&lt;/strong&gt;, una de las capacidades más recientes de IAM Access Analyzer, que permite analizar el permiso efectivo considerando SCPs, RCPs y resource policies en conjunto.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fc980gse8k9mzsibrv24z.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fc980gse8k9mzsibrv24z.png" alt=" " width="800" height="450"&gt;&lt;/a&gt;&lt;/p&gt;




&lt;h2&gt;
  
  
  &lt;strong&gt;¿Por qué importa esto para seguridad?&lt;/strong&gt;
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;Auditorías y cumplimiento (PCI-DSS, ISO 27001, SOC 2)&lt;/strong&gt;&lt;br&gt;
En una auditoría, alguien va a pedirte evidencia de quién tiene acceso a tus datos sensibles. Access Analyzer te da eso en formato exportable, con el detalle de qué acciones puede realizar cada identidad. En vez de revisar cien políticas a mano, tienes un reporte automatizado.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Least privilege de verdad&lt;/strong&gt;&lt;br&gt;
Es fácil escribir "aplicamos least privilege" en un documento de arquitectura. Es difícil demostrarlo. Con análisis de permisos efectivos puedes ver exactamente qué permisos tiene cada identidad sobre cada recurso, y limpiar lo que sobra. No lo que crees que sobra — lo que el analizador te muestra que sobra.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Detección de configuraciones erróneas antes de que las encuentre alguien más&lt;/strong&gt;&lt;br&gt;
Un rol con &lt;code&gt;"Resource": "*"&lt;/code&gt; donde debería tener un ARN específico. Una bucket policy que alguien amplió para una prueba y nunca revirtió. Un acceso cross-account que quedó abierto después de una migración. Estos hallazgos no generan alertas operacionales — el sistema funciona, el acceso existe, y nadie lo nota hasta que hay un incidente o una auditoría.&lt;/p&gt;




&lt;h2&gt;
  
  
  &lt;strong&gt;Esta serie da para más&lt;/strong&gt;
&lt;/h2&gt;

&lt;p&gt;Mientras armaba este post me di cuenta de que IAM Access Analyzer no se puede explicar bien en una sola publicación sin hacerlo pesado - la verdad los conceptos de acceso en cloud son pesados si no se aterrizan correctamente.&lt;/p&gt;

&lt;p&gt;Por eso voy a separar la serie en tres partes mas una cuarta parte opcional como bonus con mcp: External Access, Internal Access y Unused Access. En cada una veremos el concepto, un caso práctico y un pequeño lab con evidencias, para aterrizarlo mejor y no quedarnos solo en la teoría.&lt;/p&gt;

&lt;p&gt;ADVERTENCIA.&lt;br&gt;
Algunos de los labs tendrán costos por lo que les pido precaución, lean antes y tengan controlado sus recursos.&lt;/p&gt;




&lt;h2&gt;
  
  
  &lt;strong&gt;¿Qué viene en la Parte 2?&lt;/strong&gt;
&lt;/h2&gt;

&lt;p&gt;En el siguiente post vamos al laboratorio. Vamos a:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Configurar un analizador Internal Access a nivel de organización&lt;/li&gt;
&lt;li&gt;Crear tres roles con distintos niveles de acceso en una cuenta y un bucket en otra&lt;/li&gt;
&lt;li&gt;Agregar una SCP que bloquea una acción aunque IAM y la bucket policy la permitan&lt;/li&gt;
&lt;li&gt;Ver los findings de Access Analyzer y validar con CLI que el permiso efectivo es exactamente lo que el analizador reporta&lt;/li&gt;
&lt;/ul&gt;




&lt;p&gt;💡 &lt;strong&gt;Reflexión:&lt;/strong&gt; ¿Alguna vez encontraste un rol con más acceso del que debería tener, después de creer que todo estaba bien configurado? Es más común de lo que parece — y casi siempre la diferencia está entre el acceso esperado y el acceso efectivo.&lt;/p&gt;

&lt;p&gt;🔜 &lt;strong&gt;Parte 2:&lt;/strong&gt; Lab completo de IAM Access Analyzer Internal Access con SCPs, cross-account y validación por CLI.&lt;/p&gt;

&lt;h1&gt;
  
  
  AWS #CloudSecurity #IAM #AccessAnalyzer #AWSCommunityBuilder #CloudGovernance #AWSOrganizations
&lt;/h1&gt;




&lt;h2&gt;
  
  
  &lt;strong&gt;Referencias&lt;/strong&gt;
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;&lt;a href="https://docs.aws.amazon.com/IAM/latest/UserGuide/reference_policies_evaluation-logic.html" rel="noopener noreferrer"&gt;Policy evaluation logic&lt;/a&gt;&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;a href="https://docs.aws.amazon.com/IAM/latest/UserGuide/reference_policies_evaluation-logic_policy-eval-basics.html" rel="noopener noreferrer"&gt;Single account policy evaluation&lt;/a&gt;&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;a href="https://docs.aws.amazon.com/IAM/latest/UserGuide/reference_policies_evaluation-logic-cross-account.html" rel="noopener noreferrer"&gt;Cross-account policy evaluation&lt;/a&gt;&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;a href="https://docs.aws.amazon.com/IAM/latest/UserGuide/what-is-access-analyzer.html" rel="noopener noreferrer"&gt;What is IAM Access Analyzer?&lt;/a&gt;&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;a href="https://docs.aws.amazon.com/IAM/latest/UserGuide/access-analyzer-findings.html" rel="noopener noreferrer"&gt;IAM Access Analyzer findings&lt;/a&gt;&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;a href="https://docs.aws.amazon.com/IAM/latest/UserGuide/access-analyzer-resources.html" rel="noopener noreferrer"&gt;IAM Access Analyzer supported resource types&lt;/a&gt;&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;a href="https://docs.aws.amazon.com/IAM/latest/UserGuide/access-analyzer-create-internal.html" rel="noopener noreferrer"&gt;Create an IAM Access Analyzer internal access analyzer&lt;/a&gt;&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;a href="https://docs.aws.amazon.com/organizations/latest/userguide/orgs_manage_policies_scps.html" rel="noopener noreferrer"&gt;Service control policies - SCPs&lt;/a&gt;&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;a href="https://docs.aws.amazon.com/organizations/latest/userguide/orgs_manage_policies_rcps.html" rel="noopener noreferrer"&gt;Resource control policies - RCPs&lt;/a&gt;&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;a href="https://aws.amazon.com/iam/access-analyzer/pricing/" rel="noopener noreferrer"&gt;IAM Access Analyzer pricing&lt;/a&gt;&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

</description>
      <category>aws</category>
      <category>iam</category>
      <category>tutorial</category>
      <category>cybersecurity</category>
    </item>
    <item>
      <title>🛡️ Entendiendo las Service Control Policies (SCPs) en AWS Organizations</title>
      <dc:creator>Terry Quispe Paniagua</dc:creator>
      <pubDate>Tue, 02 Dec 2025 03:47:50 +0000</pubDate>
      <link>https://dev.to/terry_cloud/-entendiendo-las-service-control-policies-scps-en-aws-organizations-40li</link>
      <guid>https://dev.to/terry_cloud/-entendiendo-las-service-control-policies-scps-en-aws-organizations-40li</guid>
      <description>&lt;h2&gt;
  
  
  1. Introducción
&lt;/h2&gt;

&lt;h3&gt;
  
  
  ¿Qué problema resuelven las SCPs?
&lt;/h3&gt;

&lt;p&gt;Las SCPs, a diferencia de los permisos de IAM, no otorgan permisos, sino que los restringen. Popularmente llamados "guardrails" o "barandillas", son una herramienta clave para la gobernanza de AWS, así como también para fijar los lineamientos de seguridad y cumplimiento de la empresa.&lt;/p&gt;

&lt;p&gt;Antes de proseguir debemos entender unos conceptos previos:&lt;/p&gt;

&lt;h3&gt;
  
  
  IAM Policy
&lt;/h3&gt;

&lt;p&gt;Es un tipo de política de IAM que permite otorgar permisos a los usuarios y roles de AWS.&lt;/p&gt;

&lt;h3&gt;
  
  
  AWS Organizations
&lt;/h3&gt;

&lt;p&gt;AWS Organizations es un servicio que permite administrar y gestionar de forma centralizada múltiples cuentas de AWS. Con Organizations puedes:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Crear y gestionar cuentas de AWS (Account Management)&lt;/li&gt;
&lt;li&gt;Centralizar los costos de las cuentas de AWS (Billing)&lt;/li&gt;
&lt;li&gt;Gestionar los permisos de las cuentas de AWS junto a IAM Identity Center (Identity and Access Management)&lt;/li&gt;
&lt;li&gt;Aplicar políticas de seguridad a las cuentas de AWS (SCPs)&lt;/li&gt;
&lt;li&gt;Habilitar servicios multicuentas (Multi-account)&lt;/li&gt;
&lt;li&gt;Agrupar cuentas por Unidades Organizativas (Jerarquía de organización)&lt;/li&gt;
&lt;li&gt;Auditar entorno multicuentas (Audit)&lt;/li&gt;
&lt;li&gt;Compartir recursos en múltiples cuentas (Resource sharing)&lt;/li&gt;
&lt;li&gt;Automatizar aprovisionamiento de múltiples cuentas mediante CloudFormation (Multi-account)&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  2. Service Control Policies (SCPs)
&lt;/h2&gt;

&lt;p&gt;Es un tipo de política de AWS Organizations que permite restringir los permisos de las cuentas de AWS que pertenecen a la organización. Esta es una característica de AWS Organizations; existen otras políticas, pero nos centraremos en el tipo de servicio.&lt;/p&gt;

&lt;h3&gt;
  
  
  Estructura de una SCP
&lt;/h3&gt;

&lt;p&gt;La estructura de una SCP es la siguiente:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fpigyuysqbv3i1o352f0y.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fpigyuysqbv3i1o352f0y.png" alt="SCP Syntax" width="512" height="690"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Elementos principales de una SCP:&lt;/strong&gt;&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Elemento&lt;/th&gt;
&lt;th&gt;¿Qué hace?&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Statement&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Es el contenedor principal de una política. Puedes tener varios statements en una SCP (cuidado: hay un límite de caracteres).&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Effect&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Define si el statement permite (&lt;code&gt;Allow&lt;/code&gt;) o bloquea (&lt;code&gt;Deny&lt;/code&gt;) acciones. &lt;strong&gt;Nota:&lt;/strong&gt; &lt;code&gt;Allow&lt;/code&gt; no permite condicionales.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Action&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Especifica qué acciones de AWS se permiten o bloquean (ej: &lt;code&gt;s3:PutObject&lt;/code&gt;).&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Resource&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Indica a qué recursos de AWS aplica la política (ej: un bucket específico).&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Condition&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;(Opcional) Agrega condiciones para que el statement aplique solo en ciertos casos.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;NotAction&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Lo opuesto a &lt;code&gt;Action&lt;/code&gt;: especifica acciones que quedan exentas de la SCP.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;NotResource&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Lo opuesto a &lt;code&gt;Resource&lt;/code&gt;: especifica recursos que quedan exentos de la SCP.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Sid&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;(Opcional) Un nombre amigable para identificar el statement.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Version&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Define la versión del lenguaje de políticas (siempre usa &lt;code&gt;"2012-10-17"&lt;/code&gt;).&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;h3&gt;
  
  
  Comportamiento de una SCP
&lt;/h3&gt;

&lt;p&gt;Para entender mejor cómo funcionan las SCPs, podemos agrupar sus comportamientos en cuatro categorías clave:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;1. Principios Básicos&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Naturaleza:&lt;/strong&gt; No otorgan permisos, solo los restringen.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Evaluación:&lt;/strong&gt; Un permiso solo se ejecuta si existe una política IAM que lo permita y una SCP que lo permita o no lo deniegue, en el caso de evaluación IAM y SCP. La evaluación final se encuentra en la &lt;a href="https://docs.aws.amazon.com/es_es/IAM/latest/UserGuide/reference_policies_evaluation-logic_policy-eval-basics.html" rel="noopener noreferrer"&gt;documentación oficial de AWS&lt;/a&gt;. &lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;2. Alcance: ¿A quién afecta?&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Cuentas Miembro:&lt;/strong&gt; Las SCPs restringen los permisos de todos los usuarios y roles, &lt;strong&gt;incluido el usuario root&lt;/strong&gt;.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Administradores Delegados:&lt;/strong&gt; Sí están afectados, ya que residen en cuentas miembro.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Excepción Clave:&lt;/strong&gt; La &lt;strong&gt;Cuenta de Administración (Management Account)&lt;/strong&gt; NO se ve afectada por las SCPs.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;3. Interacción con otras Políticas&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;IAM Deny:&lt;/strong&gt; Siempre tiene prioridad sobre cualquier SCP.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;SCP Deny:&lt;/strong&gt; Bloquea la acción explícitamente, incluso si IAM otorga &lt;code&gt;AdministratorAccess&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Permission Boundaries:&lt;/strong&gt; La evaluación final requiere: &lt;strong&gt;IAM Allow + SCP Allow + Boundary Allow&lt;/strong&gt;.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;4. Excepciones Técnicas&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Service-Linked Roles:&lt;/strong&gt; No se pueden restringir con SCPs, ya que son necesarios para que los servicios de AWS funcionen correctamente.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Resource-Based Policies:&lt;/strong&gt; No se ven afectadas por SCPs; los accesos externos a recursos (como un bucket S3) siguen funcionando si la política del recurso lo permite.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Usuarios Externos:&lt;/strong&gt; No se ven afectados, incluso si acceden a recursos dentro de una cuenta restringida.&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  ¿Dónde se aplican las SCPs?
&lt;/h3&gt;

&lt;p&gt;En AWS Organizations, las SCPs se aplican a las cuentas de AWS que pertenecen a la organización y también a las unidades organizativas. Una unidad organizativa es como un folder donde puedes agrupar cuentas de AWS.&lt;/p&gt;

&lt;p&gt;A continuación una vista de una jerarquía de organización en AWS Organizations:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fbalcy273rg7fuy5l9gsp.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fbalcy273rg7fuy5l9gsp.png" alt="Image Structure Organizations" width="800" height="831"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Componentes de una jerarquía de organización:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Cuenta raíz (Root):&lt;/strong&gt; Es la cuenta principal de la organización, generalmente la cuenta de AWS que se creó al crear la organización. &lt;strong&gt;Importante:&lt;/strong&gt; Aplicar una SCP a la cuenta raíz afecta a todas las cuentas de la organización sin excepción (mucho cuidado con esto).&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Unidades organizativas (OUs):&lt;/strong&gt; Son como folders donde puedes agrupar cuentas de AWS. Aplicar una SCP a una OU afecta a todas las cuentas que pertenecen a la OU.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Cuentas:&lt;/strong&gt; Son las cuentas de AWS que pertenecen a la organización. Aplicar una SCP a una cuenta afecta solo a esa cuenta.&lt;/li&gt;
&lt;/ul&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Nota:&lt;/strong&gt; Las SCPs pueden aplicarse a múltiples cuentas por su característica de herencia.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h2&gt;
  
  
  3. Laboratorio: Creación de SCPs
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;Costo del laboratorio:&lt;/strong&gt; $0&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;⚠️ Advertencia:&lt;/strong&gt; Nunca debes aplicar una SCP a la cuenta productiva ni a un entorno con cuentas productivas. Siempre usa cuentas de prueba para validar el funcionamiento de la SCP antes de aplicarlo en múltiples cuentas.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h3&gt;
  
  
  A. Armando el laboratorio para probar SCPs
&lt;/h3&gt;

&lt;h4&gt;
  
  
  🔐 Requisitos previos
&lt;/h4&gt;

&lt;ul&gt;
&lt;li&gt;Tener una cuenta AWS principal (payer) desde la cual crearás la Organización.&lt;/li&gt;
&lt;li&gt;Tener acceso administrativo a esa cuenta.&lt;/li&gt;
&lt;li&gt;No necesitas método de pago adicional para la segunda cuenta.&lt;/li&gt;
&lt;/ul&gt;

&lt;h4&gt;
  
  
  ✅ Paso 1: Crear una Organización en AWS (si aún no está creada)
&lt;/h4&gt;

&lt;p&gt;1.1. Ingresa a la cuenta administradora (payer/root) de AWS.&lt;/p&gt;

&lt;p&gt;1.2. Ve al servicio &lt;strong&gt;AWS Organizations&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;1.3. Si todavía no tienes una organización:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Haz clic en &lt;strong&gt;Create Organization&lt;/strong&gt;.&lt;/li&gt;
&lt;li&gt;Confirmar creación de la organización.
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;🎉 Ahora tienes la capacidad de usar SCPs.&lt;/p&gt;

&lt;h4&gt;
  
  
  ✅ Paso 2: Crear una Unidad Organizativa (OU) para pruebas
&lt;/h4&gt;

&lt;p&gt;Las OU te ayudarán a aislar y probar SCPs sin afectar nada productivo o importante.&lt;/p&gt;

&lt;p&gt;2.1. En AWS Organizations, ve a &lt;strong&gt;AWS accounts&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;2.2. Selecciona la OU raíz (root) y haz clic en &lt;strong&gt;Actions&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;2.3. Clic en &lt;strong&gt;Create new organizational unit (OU)&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;2.4. Pon un nombre claro (ej: &lt;code&gt;lab-scp-test&lt;/code&gt;) y acepta.&lt;/p&gt;

&lt;h4&gt;
  
  
  ✅ Paso 3: Crear una cuenta miembro dentro de la OU
&lt;/h4&gt;

&lt;p&gt;Crearás una segunda cuenta AWS desde la cuenta administradora. Esta cuenta será tu "cuenta de pruebas" donde validarás las SCPs.&lt;/p&gt;

&lt;p&gt;3.1. Desde AWS Organizations, ve a &lt;strong&gt;AWS Accounts&lt;/strong&gt; → &lt;strong&gt;Add an AWS account&lt;/strong&gt; → &lt;strong&gt;Create an AWS account&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;3.2. Rellena:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Account name:&lt;/strong&gt; &lt;code&gt;lab-scp-member&lt;/code&gt; (o el nombre de tu elección, puedes cambiar el nombre a futuro)&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Email address:&lt;/strong&gt; Te aconsejo usar tu correo con el que creaste la cuenta administradora, pero con un alias de la siguiente manera:&lt;br&gt;
&lt;/p&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt; &amp;lt;tuemail_sin_@gmail.com&amp;gt;+labscp@gmail.com
&lt;/code&gt;&lt;/pre&gt;


&lt;p&gt;&lt;strong&gt;Ejemplo:&lt;/strong&gt; Si tu correo es &lt;code&gt;pepito@gmail.com&lt;/code&gt;, tu segunda cuenta deberá ser: &lt;code&gt;pepito+labscp@gmail.com&lt;/code&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;3.3. Clic en &lt;strong&gt;Create an AWS account&lt;/strong&gt;.&lt;/p&gt;

&lt;h4&gt;
  
  
  ✅ Paso 4: Mover la cuenta a la OU creada
&lt;/h4&gt;

&lt;p&gt;4.1. Selecciona la cuenta, clic en &lt;strong&gt;Actions&lt;/strong&gt; → &lt;strong&gt;Move AWS account&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;4.2. Elige la OU creada.&lt;/p&gt;

&lt;p&gt;4.3. Clic en &lt;strong&gt;Move AWS account&lt;/strong&gt;.&lt;/p&gt;

&lt;h4&gt;
  
  
  🔹 Configuración inicial de la cuenta miembro
&lt;/h4&gt;

&lt;p&gt;AWS enviará un correo para activar la cuenta. Necesitarás entrar a la cuenta para probar las SCPs. Para el primer ingreso lo harás con la cuenta root (podrías generar un ingreso mediante IAM Identity Center, pero se verá en otro artículo) y harás un reset de contraseña. Procura seguir las buenas prácticas de seguridad colocando MFA y una contraseña fuerte.&lt;/p&gt;

&lt;p&gt;Adicionalmente, necesitarás crear un usuario IAM con permisos de administrador, solo con propósitos de prueba, ya que no usaremos la root.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Guía para crear un usuario IAM:&lt;/strong&gt; &lt;a href="https://docs.aws.amazon.com/es_es/IAM/latest/UserGuide/id_users_create.html" rel="noopener noreferrer"&gt;https://docs.aws.amazon.com/es_es/IAM/latest/UserGuide/id_users_create.html&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Luego de crear el usuario IAM, procederás a ingresar a la cuenta miembro con el usuario IAM creado para realizar las pruebas de SCPs. Recuerda: es solo para pruebas. Las SCPs solo se adjuntarán y crearán en la cuenta administradora de la organización; no podrás ver la organización desde la cuenta miembro.&lt;/p&gt;

&lt;h3&gt;
  
  
  B. Creación de SCPs
&lt;/h3&gt;

&lt;h4&gt;
  
  
  ✅ Paso 1: Acceder a AWS Organizations
&lt;/h4&gt;

&lt;p&gt;1.1. Desde la consola de AWS, busca y selecciona &lt;strong&gt;Organizations&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;1.2. Asegúrate de estar en la cuenta raíz de la organización o en una cuenta con permisos de administración de la organización.&lt;/p&gt;

&lt;h4&gt;
  
  
  ✅ Paso 2: Crear la SCP
&lt;/h4&gt;

&lt;p&gt;2.1. En el menú lateral, selecciona &lt;strong&gt;Policies&lt;/strong&gt; → &lt;strong&gt;Service control policies&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;2.2. Haz clic en &lt;strong&gt;Create policy&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;2.3. Ingresa un nombre y descripción para tu política.&lt;/p&gt;

&lt;p&gt;2.4. Define el contenido de la política en formato JSON (ver ejemplos más adelante).&lt;/p&gt;

&lt;h3&gt;
  
  
  C. Vincular política SCP a una OU o cuenta
&lt;/h3&gt;

&lt;h4&gt;
  
  
  ✅ Paso 1: Vincular SCP a la OU de pruebas
&lt;/h4&gt;

&lt;p&gt;1.1. En &lt;strong&gt;AWS Organizations&lt;/strong&gt; → &lt;strong&gt;AWS accounts&lt;/strong&gt;, selecciona la OU objetivo, cuenta o folder raíz.&lt;/p&gt;

&lt;p&gt;1.2. Haz clic en &lt;strong&gt;Policies&lt;/strong&gt; → &lt;strong&gt;Service control policies&lt;/strong&gt; → &lt;strong&gt;Attach&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;1.3. Selecciona la SCP creada y haz clic en &lt;strong&gt;Attach policy&lt;/strong&gt;.&lt;/p&gt;

&lt;h3&gt;
  
  
  D. Prueba de SCP
&lt;/h3&gt;

&lt;p&gt;1.1. Ve a la cuenta miembro (cuenta de pruebas).&lt;/p&gt;

&lt;p&gt;1.2. Accede con el usuario IAM creado.&lt;/p&gt;

&lt;p&gt;1.3. Intenta realizar la acción que se definió en la SCP.&lt;/p&gt;

&lt;h2&gt;
  
  
  4. Ejemplos de SCP para control de recursos en AWS
&lt;/h2&gt;

&lt;p&gt;Nos basamos en una organización con estructura de OUs y cuentas de prueba, donde puedes aplicar estas SCPs para validar su funcionamiento.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Ejemplo de jerarquía organizativa:&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Root
└── OU: &amp;lt;root-ou&amp;gt;
    ├── Development
    │   ├── Sandbox Dev 01 (Cuenta)
    │   └── &amp;lt;deepracer-aft1&amp;gt; (Cuenta)
    ├── Management
    │   └── Administrator (Cuenta de administración)
    ├── Network
    │   └── Networking (Cuenta)
    └── Security
        ├── Audit (Cuenta)
        └── Log Archive (Cuenta)
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Nota:&lt;/strong&gt; Estas SCPs se pueden aplicar a la OU raíz, a una OU específica o a cuentas individuales, según el alcance que necesites. Recuerda que las denegaciones prevalecen y no se aplican a la cuenta administradora.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h3&gt;
  
  
  1️⃣ Denegar creación de buckets S3
&lt;/h3&gt;

&lt;p&gt;Evita que cualquier cuenta cree buckets S3:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"Version"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"2012-10-17"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"Statement"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
            &lt;/span&gt;&lt;span class="nl"&gt;"Sid"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"DenyS3BucketCreation"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
            &lt;/span&gt;&lt;span class="nl"&gt;"Effect"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Deny"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
            &lt;/span&gt;&lt;span class="nl"&gt;"Action"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"s3:CreateBucket"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
            &lt;/span&gt;&lt;span class="nl"&gt;"Resource"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"*"&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Aplicación:&lt;/strong&gt; Colocar en la OU raíz para afectar a todas las cuentas de prueba, bloqueando la creación de buckets S3.&lt;/p&gt;

&lt;h3&gt;
  
  
  2️⃣ Restringir regiones de despliegue en las cuentas de Development
&lt;/h3&gt;

&lt;p&gt;Evita que se utilicen regiones distintas a la permitida, dejando accesibles solo ciertos servicios esenciales:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"Version"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"2012-10-17"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"Statement"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
            &lt;/span&gt;&lt;span class="nl"&gt;"Effect"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Deny"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
            &lt;/span&gt;&lt;span class="nl"&gt;"NotAction"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="w"&gt;
                &lt;/span&gt;&lt;span class="s2"&gt;"budgets:*"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
                &lt;/span&gt;&lt;span class="s2"&gt;"cloudfront:*"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
                &lt;/span&gt;&lt;span class="s2"&gt;"config:*"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
                &lt;/span&gt;&lt;span class="s2"&gt;"ec2:DescribeRegions"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
                &lt;/span&gt;&lt;span class="s2"&gt;"ec2:DescribeTransitGateways"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
                &lt;/span&gt;&lt;span class="s2"&gt;"ec2:DescribeVpnGateways"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
                &lt;/span&gt;&lt;span class="s2"&gt;"iam:*"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
                &lt;/span&gt;&lt;span class="s2"&gt;"networkmanager:*"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
                &lt;/span&gt;&lt;span class="s2"&gt;"organizations:*"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
                &lt;/span&gt;&lt;span class="s2"&gt;"route53:*"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
                &lt;/span&gt;&lt;span class="s2"&gt;"s3:*"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
                &lt;/span&gt;&lt;span class="s2"&gt;"shield:*"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
                &lt;/span&gt;&lt;span class="s2"&gt;"sts:*"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
                &lt;/span&gt;&lt;span class="s2"&gt;"support:*"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
                &lt;/span&gt;&lt;span class="s2"&gt;"trustedadvisor:*"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
                &lt;/span&gt;&lt;span class="s2"&gt;"waf:*"&lt;/span&gt;&lt;span class="w"&gt;
            &lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;&lt;span class="w"&gt;
            &lt;/span&gt;&lt;span class="nl"&gt;"Resource"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"*"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
            &lt;/span&gt;&lt;span class="nl"&gt;"Condition"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
                &lt;/span&gt;&lt;span class="nl"&gt;"StringNotEquals"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
                    &lt;/span&gt;&lt;span class="nl"&gt;"aws:RequestedRegion"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="w"&gt;
                        &lt;/span&gt;&lt;span class="s2"&gt;"${REGION_NAME}"&lt;/span&gt;&lt;span class="w"&gt;
                    &lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="w"&gt;
                &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
            &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Reemplaza&lt;/strong&gt; &lt;code&gt;${REGION_NAME}&lt;/code&gt; por la región autorizada (ejemplo: &lt;code&gt;"us-east-1"&lt;/code&gt;).&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Aplicación:&lt;/strong&gt; Útil para controlar costos y garantizar que recursos solo se desplieguen en regiones aprobadas. Dado que mencionan el alcance hacia las cuentas Development se vinculara a la OU Development que es el folder de esas cuentas.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;descripcion:&lt;/strong&gt; En este caso se uso de NotAction para no afectar ciertos servicios de alcanza global, en este caso servicios como budgets, cloudfront, config, iam, networkmanager, organizations, route53, s3, shield, sts, support, trustedadvisor, waf no se veran afectados pero los demas si.&lt;/p&gt;

&lt;h3&gt;
  
  
  3️⃣ Restringir tipos de instancias RDS
&lt;/h3&gt;

&lt;p&gt;Evita que se creen instancias RDS que no correspondan a un tipo aprobado:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"Version"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"2012-10-17"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"Statement"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
            &lt;/span&gt;&lt;span class="nl"&gt;"Sid"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"DenyUnapprovedRDSTypes"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
            &lt;/span&gt;&lt;span class="nl"&gt;"Effect"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Deny"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
            &lt;/span&gt;&lt;span class="nl"&gt;"Action"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"rds:CreateDBInstance"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
            &lt;/span&gt;&lt;span class="nl"&gt;"Resource"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"*"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
            &lt;/span&gt;&lt;span class="nl"&gt;"Condition"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
                &lt;/span&gt;&lt;span class="nl"&gt;"StringNotEquals"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
                    &lt;/span&gt;&lt;span class="nl"&gt;"rds:DatabaseClass"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"${INSTANCEDBTYPE}"&lt;/span&gt;&lt;span class="w"&gt;
                &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
            &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Reemplaza&lt;/strong&gt; &lt;code&gt;${INSTANCEDBTYPE}&lt;/code&gt; por el tipo de instancia aprobado (ejemplo: &lt;code&gt;"db.t3.medium"&lt;/code&gt;).&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Aplicación:&lt;/strong&gt; Garantiza estándares de arquitectura y control de costos en RDS.&lt;/p&gt;

&lt;h3&gt;
  
  
  4️⃣ Denegar escritura en buckets S3 fuera de la organización
&lt;/h3&gt;

&lt;p&gt;Evita que cualquier cuenta escriba en buckets S3 que no pertenezcan a la organización:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"Version"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"2012-10-17"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"Statement"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
            &lt;/span&gt;&lt;span class="nl"&gt;"Sid"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"DenyS3WritesToUnauthorizedBuckets"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
            &lt;/span&gt;&lt;span class="nl"&gt;"Effect"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Deny"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
            &lt;/span&gt;&lt;span class="nl"&gt;"Action"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="w"&gt;
                &lt;/span&gt;&lt;span class="s2"&gt;"s3:Put*"&lt;/span&gt;&lt;span class="w"&gt;
            &lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;&lt;span class="w"&gt;
            &lt;/span&gt;&lt;span class="nl"&gt;"Resource"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"*"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
            &lt;/span&gt;&lt;span class="nl"&gt;"Condition"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
                &lt;/span&gt;&lt;span class="nl"&gt;"StringNotEquals"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
                    &lt;/span&gt;&lt;span class="nl"&gt;"aws:ResourceOrgID"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"[O-XXXXX]"&lt;/span&gt;&lt;span class="w"&gt;
                &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
            &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Reemplaza&lt;/strong&gt; &lt;code&gt;[O-XXXXX]&lt;/code&gt; por el ID de tu organización.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Aplicación:&lt;/strong&gt; Útil para prevenir que los datos se escriban fuera de los buckets gestionados por tu organización, reforzando la seguridad y gobernanza de S3.&lt;/p&gt;

&lt;h2&gt;
  
  
  5. SCPs que toda cuenta debería tener
&lt;/h2&gt;

&lt;p&gt;No existen SCPs que toda cuenta debería tener de forma universal, ya que cada organización tiene sus propias necesidades y requisitos. Sin embargo, bajo un modelo de seguridad básico, considero que las cuentas deberían tener al menos las siguientes SCPs:&lt;/p&gt;

&lt;h3&gt;
  
  
  🔐 5.1. Denegar la utilización de cuentas ROOT
&lt;/h3&gt;

&lt;p&gt;&lt;strong&gt;Descripción:&lt;/strong&gt; Evita que se realicen acciones críticas usando la cuenta raíz de AWS.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Beneficio:&lt;/strong&gt; Protege la cuenta administrativa principal de accesos accidentales o maliciosos.&lt;/p&gt;




&lt;h3&gt;
  
  
  🚪 5.2. Denegar la acción de salir de la organización
&lt;/h3&gt;

&lt;p&gt;&lt;strong&gt;Descripción:&lt;/strong&gt; Impide que las cuentas miembros abandonen la organización.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Beneficio:&lt;/strong&gt; Mantiene la gobernanza centralizada y evita pérdidas de control sobre cuentas.&lt;/p&gt;




&lt;h3&gt;
  
  
  👤 5.3. Denegar acciones en IAM que no sean usuarios y roles específicos
&lt;/h3&gt;

&lt;p&gt;&lt;strong&gt;Descripción:&lt;/strong&gt; Restringe la creación o modificación de políticas, roles y permisos que no estén permitidos.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Beneficio:&lt;/strong&gt; Evita escalación de privilegios no controlada y mantiene la seguridad de la identidad.&lt;/p&gt;




&lt;h3&gt;
  
  
  🌍 5.4. Denegar regiones no utilizadas en la organización
&lt;/h3&gt;

&lt;p&gt;&lt;strong&gt;Descripción:&lt;/strong&gt; Bloquea la creación de recursos fuera de las regiones aprobadas.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Beneficio:&lt;/strong&gt; Controla costos y asegura consistencia operativa en regiones autorizadas.&lt;/p&gt;




&lt;h3&gt;
  
  
  📝 5.5. Denegar borrado y modificación de registros específicos
&lt;/h3&gt;

&lt;p&gt;&lt;strong&gt;Descripción:&lt;/strong&gt; Protege logs críticos de CloudTrail, Config u otros registros importantes.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Beneficio:&lt;/strong&gt; Garantiza trazabilidad y cumplimiento normativo.&lt;/p&gt;




&lt;h3&gt;
  
  
  🗄️ 5.6. Denegar borrado de buckets donde se guardan registros de auditoría
&lt;/h3&gt;

&lt;p&gt;&lt;strong&gt;Descripción:&lt;/strong&gt; Impide que se eliminen buckets S3 usados para almacenar logs de auditoría.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Beneficio:&lt;/strong&gt; Asegura la integridad de los registros y evidencia de auditoría.&lt;/p&gt;




&lt;h3&gt;
  
  
  💰 5.7. Denegar servicios con alto coste no útiles para la organización
&lt;/h3&gt;

&lt;p&gt;&lt;strong&gt;Descripción:&lt;/strong&gt; Bloquea servicios como Redshift, SageMaker u otros que no estén aprobados.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Beneficio:&lt;/strong&gt; Evita gastos innecesarios y recursos no autorizados.&lt;/p&gt;

&lt;p&gt;Aunque muchas de estas SCPs están enfocadas principalmente en gobernanza y control, también actúan como capas adicionales de seguridad, creando barreras que protegen la organización de errores, mal uso o accesos no deseados.&lt;/p&gt;

&lt;h2&gt;
  
  
  6. Conclusión
&lt;/h2&gt;

&lt;p&gt;Las Service Control Policies (SCPs) son una herramienta fundamental para establecer guardrails de seguridad, cumplimiento y gobernanza en AWS Organizations. A través de este artículo hemos aprendido:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Qué son las SCPs&lt;/strong&gt; y cómo se diferencian de las políticas IAM tradicionales&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Cómo funcionan&lt;/strong&gt; y a quién afectan dentro de la organización&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Dónde aplicarlas&lt;/strong&gt; en la jerarquía organizativa&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Cómo crear un laboratorio&lt;/strong&gt; de pruebas sin costo para validar SCPs&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Ejemplos prácticos&lt;/strong&gt; de políticas comunes para controlar recursos&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Recomendaciones básicas&lt;/strong&gt; de SCPs que toda organización debería considerar&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Recuerda siempre probar tus SCPs en entornos de prueba antes de aplicarlas en producción, ya que una política mal configurada puede bloquear operaciones críticas. Las SCPs son restrictivas por naturaleza: no otorgan permisos, solo los limitan.&lt;/p&gt;

&lt;p&gt;Con una implementación cuidadosa, las SCPs te permitirán mantener el control centralizado de tu entorno multi-cuenta, reducir riesgos de seguridad y optimizar costos operativos.&lt;/p&gt;

&lt;h2&gt;
  
  
  7. Bonus RedTeam
&lt;/h2&gt;

&lt;p&gt;Hemos hablado bastante de SCPs, pero ahora comprendes que tan importante es la management account para controlar todo el entorno. Si alguien tiene acceso no autorizado a la management account es lo mas peligroso que puede pasar, como recordaras estan excentas de las SCPs y puedes tomar acciones en las cuentas miembros desde crearlas, cerrarlas o expulsarlas de la organización. AWS organizations no es solo politicas de organización, es un servicio que integra muchos otro servicio de seguridad que veremos mas adelante.&lt;/p&gt;

&lt;h2&gt;
  
  
  8. Pregunta de reflexión final
&lt;/h2&gt;

&lt;p&gt;¿Qué tan seguro crees que es tu entorno multi-cuenta? ¿Qué medidas adicionales consideras importantes para mejorar la seguridad de tu organización?&lt;/p&gt;

&lt;p&gt;🚀 &lt;strong&gt;¡Ahora es tu turno de implementar SCPs en tu organización!&lt;/strong&gt;&lt;/p&gt;




&lt;h2&gt;
  
  
  9. Documentación y Referencias
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://docs.aws.amazon.com/es_es/IAM/latest/UserGuide/reference_policies_evaluation-logic_policy-eval-basics.html" rel="noopener noreferrer"&gt;AWS IAM - Lógica de evaluación de políticas&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://docs.aws.amazon.com/es_es/organizations/latest/userguide/orgs_tutorials_basic.html" rel="noopener noreferrer"&gt;AWS Organizations - Tutorial básico&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://docs.aws.amazon.com/es_es/organizations/latest/userguide/orgs_manage_policies_scps.html" rel="noopener noreferrer"&gt;AWS Organizations - Gestión de Service Control Policies&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://aws-samples.github.io/threat-technique-catalog-for-aws/Techniques/T1666.A002.html" rel="noopener noreferrer"&gt;AWS Threat Technique Catalog - Técnicas de amenazas en Organizations&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://docs.aws.amazon.com/organizations/latest/userguide/orgs_manage_policies_scps_examples_general.html" rel="noopener noreferrer"&gt;AWS Organizations - Ejemplos de SCPs&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

</description>
      <category>aws</category>
      <category>management</category>
      <category>security</category>
    </item>
    <item>
      <title>AWS Policy Deep Dive</title>
      <dc:creator>Terry Quispe Paniagua</dc:creator>
      <pubDate>Sun, 23 Nov 2025 23:37:11 +0000</pubDate>
      <link>https://dev.to/terry_cloud/aws-policy-deep-dive-5e78</link>
      <guid>https://dev.to/terry_cloud/aws-policy-deep-dive-5e78</guid>
      <description>&lt;h2&gt;
  
  
  &lt;strong&gt;Parte 1: ¿Crees que tus datos están seguros solo con IAM? Hablemos de SCPs y RCPs.&lt;/strong&gt;
&lt;/h2&gt;

&lt;p&gt;🛡️ La seguridad en la nube requiere una estrategia de defensa en profundidad. 🛡️&lt;br&gt;
Cuando empecé en AWS, pensaba que con IAM bastaba. Pero al profundizar, me di cuenta de la importancia de capas adicionales como las SCPs y RCPs.&lt;/p&gt;

&lt;p&gt;Entender la diferencia entre&amp;nbsp;&lt;strong&gt;Identidad&lt;/strong&gt;,&amp;nbsp;&lt;strong&gt;Recurso&lt;/strong&gt;&amp;nbsp;y&amp;nbsp;&lt;strong&gt;Control Organizacional&lt;/strong&gt;&amp;nbsp;es vital para proteger nuestros entornos. No es solo "dar permisos", es saber&amp;nbsp;&lt;em&gt;dónde&lt;/em&gt;&amp;nbsp;aplicarlos para garantizar el menor privilegio y la máxima seguridad.&lt;/p&gt;

&lt;p&gt;Aquí les comparto mi guía mental para no perderse y saber identificar cuando podemos o debemos usar cada una de estas.&lt;/p&gt;

&lt;h3&gt;
  
  
  1️⃣ Identity-based Policies (IAM Policies)
&lt;/h3&gt;

&lt;p&gt;👉&amp;nbsp;&lt;strong&gt;El "Quién puede hacer qué"&lt;/strong&gt;&amp;nbsp;Son las más comunes. Se adjuntan a Usuarios, Grupos o Roles. &lt;br&gt;
📝&amp;nbsp;&lt;strong&gt;Ejemplo:&lt;/strong&gt;&amp;nbsp;Un usuario de operaciones necesita iniciar o detener instancias EC2s, pero no puede crear, terminar ni modificar instancias.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Acción:&lt;/strong&gt;&amp;nbsp;Crear una Policy que permita&amp;nbsp;&lt;code&gt;ec2:StartInstances&lt;/code&gt;&amp;nbsp; y &lt;code&gt;ec2:StopInstances&lt;/code&gt; en la tabla de "Productos".&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Resultado:&lt;/strong&gt;&amp;nbsp;Si intenta modificar o eliminar la instancia, AWS le dice "No".&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  2️⃣ Resource-based Policies
&lt;/h3&gt;

&lt;p&gt;👉&amp;nbsp;&lt;strong&gt;El "Quién puede tocar ESTO"&lt;/strong&gt;&amp;nbsp;Aquí la regla vive en el recurso mismo (S3 Bucket, SQS, KMS Key), no en el usuario. Son vitales para accesos&amp;nbsp;&lt;em&gt;Cross-Account&lt;/em&gt; pero no restringidos a ellos. 📝&amp;nbsp;&lt;strong&gt;Ejemplo:&lt;/strong&gt;&amp;nbsp;Tienes un Bucket S3 de logs centralizados.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Acción:&lt;/strong&gt;&amp;nbsp;Colocas una Bucket Policy que dice: "Permitir&amp;nbsp;&lt;code&gt;s3:PutObject&lt;/code&gt;&amp;nbsp;a la cuenta de Producción (Account B)".&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Resultado:&lt;/strong&gt;&amp;nbsp;La cuenta B puede escribir logs ahí sin tener un usuario creado en tu cuenta de logs.&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  3️⃣ Service Control Policies (SCPs)
&lt;/h3&gt;

&lt;p&gt;👉&amp;nbsp;&lt;strong&gt;Las "Reglas de la Casa" (Límites de Identidad, popularmente guardrail o barandilla de seguridad)&lt;/strong&gt;&amp;nbsp;Aquí es donde muchos se confunden. Las SCPs&amp;nbsp;&lt;strong&gt;NO&lt;/strong&gt;&amp;nbsp;dan permisos. Solo definen el&amp;nbsp;&lt;em&gt;máximo&lt;/em&gt;&amp;nbsp;permiso posible. Si la SCP dice "No", no importa si tienes AdminAccess, es un "No". 📝&amp;nbsp;&lt;strong&gt;Ejemplo:&lt;/strong&gt;&amp;nbsp;Seguridad Compliance.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Acción:&lt;/strong&gt;&amp;nbsp;Aplicas una SCP en la raíz de tu Organización que niega&amp;nbsp;&lt;code&gt;ec2:RunInstances&lt;/code&gt;&amp;nbsp;en cualquier región que no sea&amp;nbsp;&lt;code&gt;us-east-1&lt;/code&gt;.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  - &lt;strong&gt;Resultado:&lt;/strong&gt;&amp;nbsp;Aunque seas Admin, si intentas levantar un servidor en Tokyo, fallará.
&lt;/h2&gt;

&lt;h3&gt;
  
  
  &amp;nbsp;Resource Control Policies (RCPs)
&lt;/h3&gt;

&lt;p&gt;👉&amp;nbsp;&lt;strong&gt;El "Perímetro de Datos" (Límites de Recurso)&lt;/strong&gt;&amp;nbsp;Funcionan como las SCPs, pero enfocadas en restringir el acceso a tus&amp;nbsp;&lt;strong&gt;recursos&lt;/strong&gt;, sin importar quién sea el que llama (incluso si es externo). 📝&amp;nbsp;&lt;strong&gt;Ejemplo:&lt;/strong&gt;&amp;nbsp;Data Perimeter estricto.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Acción:&lt;/strong&gt;&amp;nbsp;Creas una RCP que dice: "Nuestros Buckets S3 solo pueden ser accedidos por&amp;nbsp;&lt;em&gt;Principals&lt;/em&gt;&amp;nbsp;que pertenezcan a nuestra Organización (PrincipalOrgID)".&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Resultado:&lt;/strong&gt;&amp;nbsp;Si alguien configura por error un Bucket como "Público" o intenta darle acceso a una cuenta externa de un proveedor, la RCP lo bloquea automáticamente. Ideal para prevenir ataques de exfiltración de datos&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  &amp;nbsp;Resumen
&lt;/h2&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Tipo de Política&lt;/th&gt;
&lt;th&gt;Se aplica a...&lt;/th&gt;
&lt;th&gt;Función Principal&lt;/th&gt;
&lt;th&gt;¿Cuándo usarlo?&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;IAM Policy&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Usuario/Rol/Grupo&lt;/td&gt;
&lt;td&gt;Otorgar permisos&lt;/td&gt;
&lt;td&gt;Para otorgar permisos a usuarios, grupos o roles&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Resource Policy&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;S3, SQS, etc.&lt;/td&gt;
&lt;td&gt;Otorgar acceso (incluso externo)&lt;/td&gt;
&lt;td&gt;Para definir quien puede acceder a un recurso&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;SCP&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Cuenta AWS (excepto cuenta administradora de la organización), Unidades Org&lt;/td&gt;
&lt;td&gt;Restringir permisos máximos (Identidad)&lt;/td&gt;
&lt;td&gt;Para establecer límites máximos sobre las identidades&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;RCP&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Recursos de la Org, excepto recursos de la cuenta administradora de la organización&lt;/td&gt;
&lt;td&gt;Restringir acceso máximo (Recurso)&lt;/td&gt;
&lt;td&gt;Para establecer límites máximos sobre sobre quien accede a tus datos&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;h3&gt;
  
  
  🔥 Bonus: Perspectiva Red Team
&lt;/h3&gt;

&lt;p&gt;Entender IAM, SCPs y RCPs no solo es vital para proteger tu cuenta y organización, también es un componente clave para reducir la superficie de ataque.&lt;/p&gt;

&lt;p&gt;Por ejemplo:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Escenario de Red Team:&lt;/strong&gt; un atacante intenta asumir roles o crear usuarios para escalar privilegios.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;SCPs bloquean el movimiento lateral:&lt;/strong&gt; aunque tenga AdminAccess en una cuenta hija, no podrá ejecutar acciones prohibidas por la SCP a nivel de Organización (ej: &lt;code&gt;organizations:LeaveOrganization&lt;/code&gt;, &lt;code&gt;iam:CreateUser&lt;/code&gt;).&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;RCPs evitan exfiltración de datos:&lt;/strong&gt; un bucket mal configurado no permite accesos de cuentas externas aunque el atacante tenga privilegios en otra parte.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Conclusión:&lt;/strong&gt; conocer y aplicar correctamente estas políticas es &lt;strong&gt;como poner murallas antes de que alguien llegue al nucleo de tu entorno&lt;/strong&gt;.&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;💡 Pregunta de reflexión: ¿por qué es un riesgo crítico desplegar recursos productivos o mantener usuarios activos operando directamente en la cuenta administradora de la organización o también llamado Payer Account?&lt;/p&gt;

&lt;p&gt;🔜&amp;nbsp;&lt;strong&gt;En el próximo post:&lt;/strong&gt;&amp;nbsp;Profundizaremos en las&amp;nbsp;&lt;strong&gt;SCPs&lt;/strong&gt;, cuales son las SCPs mas relevantes que toda cuenta debería tener y cómo configurarlas correctamente para dormir tranquilos.&lt;/p&gt;

</description>
      <category>aws</category>
      <category>cloud</category>
      <category>security</category>
    </item>
  </channel>
</rss>
