Spanish version followed by English version.
Durante el diseño de una solución crítica basada en AWS Lambda, surgió una pregunta poco habitual: ¿es posible garantizar que un rol de servicio para pueda ser utilizado por una única función, incluso dentro de la misma cuenta de AWS?
En este artículo analizo el problema y presentó una solución basada en políticas de confianza que respeta el comportamiento interno de AWS Lambda, incluyendo escenarios con configuración de VPC.
Voy a desglosar el contexto técnico y de arquitectura que derivó en la necesidad.
El contexto
Dentro de la organización hay una arquitectura empresarial que define una gran cantidad de cuentas de AWS por área y ambiente. Debido al enorme tamaño de la organización, una gran cantidad de equipos tiene acceso para desplegar recursos dentro de una misma cuenta.
La función lambda en cuestión tiene como objetivo realizar diversas operaciones administrativas; estas operaciones se ejecutan sobre una plataforma de uso transversal dentro de toda organización, por lo que se vuelve una solución bastante crítica y sensible.
Los permisos administrativos sobre la plataforma transversal se otorgan mediante el rol de servicio IAM de la función lambda. Debido a que otros equipos pueden desplegar recursos en la misma cuenta, se podría hacer uso indebido de este rol con accesos privilegiados.
Recordemos como se ve la política de confianza de un rol de servicio estándar de AWS Lambda
{
"Version": "2012-10-17",
"Statement": [
{
"Effect": "Allow",
"Principal": {
"Service": "lambda.amazonaws.com"
}
]
}
Esta política de confianza de un rol IAM permite que cualquier función lambda dentro de la misma cuenta de AWS pueda hacer uso del mismo.
Primer intento: Denegación explícita
¿Cómo asegurar el rol para que no pueda ser usado por otra función dentro de la misma cuenta? Lo primero que se me ocurrió fue colocar una declaración adicional a la política de confianza que aplicará una denegación explícita a la acción assumeRole a todas las entidades de AWS, excepto a la autorizada. Use el operador ArnNotEquals dentro de una condición para lograr esto. El efecto final esperado es que se deniegue la acción para asumir el rol a cualquier entidad, recordemos que una denegación explícita prevalece sobre cualquier permiso concedido explícitamente, excepto a la función lambda autorizada.
{
"Version": "2012-10-17",
"Statement": [
{
"Effect": "Allow",
"Principal": {
"Service": "lambda.amazonaws.com"
},
"Action": "sts:AssumeRole"
},
{
"Sid": "ExplicitDenyForAllExceptAuthorizedLambda",
"Effect": "Deny",
"Principal": "*",
"Action": "sts:AssumeRole",
"Condition": {
"ArnNotEquals": {
"aws:SourceArn": "arn:aws:lambda:REGION:ACCOUNT_ID:function:AUTHORIZED_LAMBDA_NAME"
}
}
}
]
}
una vez se desplegó el rol con esta nueva política de confianza la función lambda no tuvo ningún inconveniente para usar el rol de servicio y a su vez, cuando cualquier otra función lambda se intentaba desplegar asociando ese mismo rol de servicio, Obtenía un error al momento de desplegar por falta de permisos. En resumen se logró el comportamiento esperado
El problema de VPC
Hasta ese punto todo bien ¿verdad? Resulta que por aspectos técnicos de la plataforma transversal, sobre la cual la función debe ejecutar operaciones, la función debía desplegarse con configuración de VPC para desplegarse en la red interna de la cuenta de AWS. Resulta que al añadir la configuración de VPC a la función lambda, se presentó un error en el despliegue relacionado a la falta de permisos para asumir el rol.
Entonces ¿Qué sucedió? resulta que al agregar una configuración de VPC a la función Lambda. El servicio ejecuta una serie de acciones de forma subyacente, estas acciones deben hacer uso del service rol de la lambda para crear las interfaces de red, asignar IPs y demás; Estas acciones son ejecutadas por recursos administrados por AWS, recursos que están incluidos en la definición de servicio: "Service": "lambda.amazonaws.com".
Sin embargo, la declaración que aplica la denegación sólo excluye la ARN de la función Lambda. Se necesita que el rol pueda ser usado por recursos adicionales de VPC asociados al servicio de AWS Lambda. Investigando cómo ajustar una condición que haga esto posible, encontré la jerarquía de operadores lógicos que se aplican en una condición IAM compuesta. Resulta que cuando se hace uso de múltiples operadores (ArnEquals, ArnNotequals, StringLike) dentro de una misma condición, la condición aplica una cadena de operación logica que aplica un AND para cada uno de los operadores.
ArnEquals(value) AND ArnNotequals(value) AND StringLike(value)
Solución final
Bajo esta premisa construí una condición que se adapta a la necesidad de restringir el uso de un rol para una única función lambda dentro de la misma cuenta.
{
"Version": "2012-10-17",
"Statement": [
{
"Effect": "Allow",
"Principal": {
"Service": "lambda.amazonaws.com"
},
"Action": "sts:AssumeRole"
},
{
"Sid": "DenyAllOtherLambdasExceptAuthorizedOne",
"Effect": "Deny",
"Principal": "*",
"Action": "sts:AssumeRole",
"Condition": {
"ArnLike": {
"aws:PrincipalArn": "arn:aws:lambda:*:*:function:*"
},
"ArnNotEquals": {
"aws:PrincipalArn": "arn:aws:lambda:<REGION>:<ACCOUNT_ID>:function:<AUTHORIZED_LAMBDA_NAME>"
}
}
}
]
}
Desglose de esta política de confianza:
La declaración que aplica un Allow es la estándar para un rol de servicio de AWS Lambda
La segunda declaración aplica un Deny es la que solventa la necesidad correctamente
Al tener dos operadores: ArnLike y ArnNotEquals, estos dos deben cumplirse para que la denegación sea efectiva
ArnLike: lo primero que se valida es que la entidad que trata de hacer uso del rol corresponda a la ARN de una función lambda, de esta manera no se deja inutilizable el rol en caso de que se use para una función con configuración VPC
ArnNotEquals: Aquí se valida que la función lambda que esté haciendo uso del rol no corresponda a la función autorizada, de esta manera una función diferente desplegada por otro equipo no podrá hacer uso indebido de el rol privilegiado.
Aplicando esta política de confianza al rol se logra el comportamiento esperado, el rol de servicio sólo puede ser usado por una única función lambda dentro de la cuenta de AWS.
English Version
During the design of a critical solution based on AWS Lambda, an unusual question arose: is it possible to guarantee that a service role can only be used by one specific function, even within the same AWS account?
In this article, I analyze this problem and present a solution based on trust policies that respects AWS Lambda's internal behavior, including scenarios with VPC configurations. I will break down the technical and architectural context that led to this requirement.
The Context
Within the organization, there is an enterprise architecture that defines a large number of AWS accounts per area and environment. Due to the massive size of the organization, many teams have access to deploy resources within the same account.
The Lambda function in question aims to perform various administrative operations; these operations run on a cross-cutting platform used throughout the organization, making it a critical and sensitive solution.
Administrative permissions on this platform are granted through the Lambda function's IAM service role. Since other teams can deploy resources in the same account, this privileged role could potentially be misused.
Let’s recall what a standard AWS Lambda service role trust policy looks like:
{
"Version": "2012-10-17",
"Statement": [
{
"Effect": "Allow",
"Principal": {
"Service": "lambda.amazonaws.com"
}
]
}
This IAM role trust policy allows any Lambda function within the same AWS account to use it.
The First Attempt: Explicit Deny
How can we secure the role so it cannot be used by another function in the same account? My first thought was to add an additional statement to the trust policy to apply an explicit deny on the AssumeRole action for all AWS entities except the authorized one. I used the ArnNotEquals operator within a condition to achieve this.
The expected result is to deny the role assumption for any entity—remembering that an explicit deny overrides any explicitly granted permission—except for the authorized Lambda function.
{
"Version": "2012-10-17",
"Statement": [
{
"Effect": "Allow",
"Principal": {
"Service": "lambda.amazonaws.com"
},
"Action": "sts:AssumeRole"
},
{
"Sid": "ExplicitDenyForAllExceptAuthorizedLambda",
"Effect": "Deny",
"Principal": "*",
"Action": "sts:AssumeRole",
"Condition": {
"ArnNotEquals": {
"aws:SourceArn": "arn:aws:lambda:REGION:ACCOUNT_ID:function:AUTHORIZED_LAMBDA_NAME"
}
}
}
]
}
Once the role was deployed with this new trust policy, the Lambda function had no issues using the service role. Simultaneously, when any other Lambda function tried to deploy while associating this same service role, it triggered a deployment error due to a lack of permissions. In short, the expected behavior was achieved.
The VPC Challenge
Everything looked good so far, right? However, due to technical requirements of the platform the function interacts with, the Lambda had to be deployed with a VPC configuration to access the internal network.
As soon as the VPC configuration was added, a deployment error appeared regarding the lack of permissions to assume the role.
So, what happened? When you add a VPC configuration to a Lambda function, the service executes several underlying actions. These actions must use the Lambda's service role to create Elastic Network Interfaces (ENIs), assign IPs, and perform other management tasks. These actions are performed by AWS-managed resources included in the service definition: "Service": "lambda.amazonaws.com".
The problem was that the statement applying the deny only excluded the specific Lambda function ARN. The role also needs to be usable by the additional VPC-related resources associated with the AWS Lambda service.
While researching how to adjust the condition, I looked into the hierarchy of logical operators in composite IAM conditions. It turns out that when using multiple operators (such as ArnLike and ArnNotEquals) within the same condition block, IAM applies a logical AND between them.
ArnLike(value) AND ArnNotEquals(value)
The Final Solution
Based on this premise, I constructed a condition that restricts the role to a single Lambda function while still allowing the service to perform its background operations.
{
"Version": "2012-10-17",
"Statement": [
{
"Effect": "Allow",
"Principal": {
"Service": "lambda.amazonaws.com"
},
"Action": "sts:AssumeRole"
},
{
"Sid": "DenyAllOtherLambdasExceptAuthorizedOne",
"Effect": "Deny",
"Principal": "*",
"Action": "sts:AssumeRole",
"Condition": {
"ArnLike": {
"aws:PrincipalArn": "arn:aws:lambda:*:*:function:*"
},
"ArnNotEquals": {
"aws:PrincipalArn": "arn:aws:lambda:<REGION>:<ACCOUNT_ID>:function:<AUTHORIZED_LAMBDA_NAME>"
}
}
}
]
}
Breaking down this policy:
The Allow statement: Standard for an AWS Lambda service role.
-
The Deny statement: This correctly solves the requirement. Since we use two operators, both must be true for the denial to trigger:
- ArnLike: First, it validates if the entity attempting to use the role matches a Lambda function ARN pattern. This prevents the role from becoming unusable when background VPC-related processes (which don't match this specific ARN pattern) need to act.
- ArnNotEquals: It then checks if the Lambda function using the role is not the authorized one.
By applying this trust policy, the service role is successfully locked down to a single Lambda function within the AWS account, even when using VPC configurations.




Top comments (1)
Hola Juan,
Excelente artículo y muy clara la explicación sobre cómo endurecer la trust policy del rol de servicio utilizado por AWS Lambda, un punto crítico que con frecuencia se pasa por alto en arquitecturas serverless.
Desde una perspectiva de ciberseguridad, este enfoque encaja bien dentro de un modelo de defensa en profundidad y Zero Trust, donde incluso los servicios dentro de la misma cuenta deben autenticarse y autorizarse explícitamente. Limitar quién puede asumir el rol reduce significativamente la superficie de ataque y previene abusos laterales entre servicios.
Como complemento al planteamiento del artículo, vale la pena destacar: