You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Terraform中AWS Lambda权限策略超限问题及解决方案咨询

背景信息

我有一个名为console的Lambda函数,用于通过PHP Symfony的控制台命令运行所有定时任务。由于代码库是大型单体应用,为每个命令单独创建Lambda仍需包含99.9%的相同代码,部署难度极大,因此只使用这一个Lambda函数。

该Lambda函数由CloudWatch Events触发,基本是1条CloudWatch规则对应1个任务。由于我们还有无关的CloudWatch规则,希望确保只有指定规则可以触发该Lambda函数,不允许所有CloudWatch规则调用它。

我们使用Terraform进行管理,代码结构如下:

resource "aws_lambda_function" "console" {
   # 无关配置省略
}

# 以下3个资源会为每个任务重复N次
resource "aws_cloudwatch_event_rule" "run_task_foobar" {
  # 无关配置省略
}

resource "aws_cloudwatch_event_target" "run_task_foobar" {
  rule      = aws_cloudwatch_event_rule.run_task_foobar.name
  target_id = "foobar"
  arn       = aws_lambda_function.console.arn
  input     = "\" console run foobar \""
}

resource "aws_lambda_permission" "run_task_foobar" {
  action        = "lambda:InvokeFunction"
  function_name = aws_lambda_function.console.function_name
  principal     = "events.amazonaws.com"
  source_arn    = aws_cloudwatch_event_rule.run_task_foobar.arn

  # 不支持标签
}
问题描述

目前我们共有53个任务,当尝试添加第54个任务时,出现以下错误:

PolicyLengthExceededException: The final policy size (20701) is bigger than the limit (20480).

在AWS控制台中可以看到生成的IAM策略非常庞大,示例如下:

{
  "Version": "2012-10-17",
  "Id": "default",
  "Statement": [
    {
      "Sid": "foo",
      "Effect": "Allow",
      "Principal": {
        "Service": "events.amazonaws.com"
      },
      "Action": "lambda:InvokeFunction",
      "Resource": "arn:aws:lambda:eu-west-1:xxxx:function:console",
      "Condition": {
        "ArnLike": {
          "AWS:SourceArn": "arn:aws:events:eu-west-1:xxxxx:rule/foo"
        }
      }
    },
    {
      "Sid": "bar",
      "Effect": "Allow",
      "Principal": {
        "Service": "events.amazonaws.com"
      },
      "Action": "lambda:InvokeFunction",
      "Resource": "arn:aws:lambda:eu-west-1:xxxx:function:console",
      "Condition": {
        "ArnLike": {
          "AWS:SourceArn": "arn:aws:events:eu-west-1:xxxxx:rule/bar"
        }
      }
    },
    ... // 重复53次
  ]
}

显然应该将Condition块合并为包含多个SourceArn的数组形式,伪代码如下:

{
  "Version": "2012-10-17",
  "Id": "default",
  "Statement": [
    {
      "Sid": "AllowConsoleTaskRules",
      "Effect": "Allow",
      "Principal": {
        "Service": "events.amazonaws.com"
      },
      "Action": "lambda:InvokeFunction",
      "Resource": "arn:aws:lambda:eu-west-1:xxxx:function:console",
      "Condition": {
        "ArnEquals": {
          "AWS:SourceArn": [
               "arn:aws:events:eu-west-1:xxxxx:rule/bar",
               "arn:aws:events:eu-west-1:xxxxx:rule/foo",
               ... // 53个ARN
           ]
        }
      }
    }
  ]
}

但Terraform和AWS控制台均自动生成独立的Statement条目,无法对Condition进行精细控制。

问题

  1. 是否存在实际可行的合并策略的方法?
  2. 是否可以通过直接创建IAM策略来作为替代方案?

我当前的临时解决方案是约定所有相关CloudWatch事件规则以"console-"开头,使用通配符匹配,但这似乎不够稳健。


解决方案

1. 合并策略的可行方法

可以通过Terraform直接构建Lambda的资源策略文档,将所有CloudWatch规则的ARN合并到同一个Condition中,替代逐个创建aws_lambda_permission资源的方式。

具体实现思路:

  • 先将所有CloudWatch事件规则的ARN收集到一个列表中(如果是通过for_each批量创建规则,可直接通过values(aws_cloudwatch_event_rule.run_task_*)获取所有ARN)
  • 使用jsonencode构建完整的IAM策略文档,在Condition的ArnEquals中使用ARN数组
  • 将该策略文档直接赋值给aws_lambda_function的policy参数

示例Terraform代码:

# 假设通过for_each批量创建CloudWatch规则
locals {
  tasks = {
    foobar = "console run foobar"
    baz    = "console run baz"
    # 其他任务...
  }
}

resource "aws_cloudwatch_event_rule" "run_task" {
  for_each = local.tasks
  name     = "run-task-${each.key}"
  # 其他规则配置(比如schedule_expression)
}

resource "aws_cloudwatch_event_target" "run_task" {
  for_each = local.tasks
  rule      = aws_cloudwatch_event_rule.run_task[each.key].name
  target_id = each.key
  arn       = aws_lambda_function.console.arn
  input     = "\"${each.value}\""
}

# 构建合并后的Lambda资源策略
resource "aws_lambda_function" "console" {
  # 原有配置(filename, handler, runtime等)

  policy = jsonencode({
    Version = "2012-10-17"
    Id      = "default"
    Statement = [
      {
        Sid       = "AllowConsoleTaskRules"
        Effect    = "Allow"
        Principal = { Service = "events.amazonaws.com" }
        Action    = "lambda:InvokeFunction"
        Resource  = self.arn
        Condition = {
          ArnEquals = {
            "AWS:SourceArn" = values(aws_cloudwatch_event_rule.run_task)[*].arn
          }
        }
      }
    ]
  })
}

这种方式会生成一条包含所有CloudWatch规则ARN的Statement,避免策略大小超限,同时严格限定了允许触发Lambda的规则范围。

2. 直接创建IAM策略作为替代方案

是的,可以通过单独创建IAM策略资源,然后将其作为Lambda的资源策略附加。注意Lambda的资源策略是直接关联到函数本身的,而非通过IAM角色(IAM角色策略控制Lambda能访问的资源,资源策略控制哪些实体能调用Lambda)。

示例代码:

# 定义合并后的IAM策略
resource "aws_iam_policy" "console_lambda_invoke" {
  name        = "console-lambda-invoke-policy"
  description = "Allow specific CloudWatch Events rules to invoke the console Lambda"

  policy = jsonencode({
    Version = "2012-10-17"
    Statement = [
      {
        Sid       = "AllowConsoleTaskRules"
        Effect    = "Allow"
        Principal = { Service = "events.amazonaws.com" }
        Action    = "lambda:InvokeFunction"
        Resource  = aws_lambda_function.console.arn
        Condition = {
          ArnEquals = {
            "AWS:SourceArn" = values(aws_cloudwatch_event_rule.run_task)[*].arn
          }
        }
      }
    ]
  })
}

# 将策略附加到Lambda函数
resource "aws_lambda_function" "console" {
  # 原有配置
  policy = aws_iam_policy.console_lambda_invoke.policy
}

这种方式和直接在Lambda函数中定义策略效果一致,好处是可以将策略单独管理,便于复用或修改。

注意事项

  • 如果之前已创建多个aws_lambda_permission资源,需先删除这些资源,避免新旧策略冲突
  • 使用values(aws_cloudwatch_event_rule.run_task)[*].arn可自动收集所有通过for_each创建的规则ARN,无需手动维护列表
  • AWS的Condition支持ArnEquals(精确匹配)或ArnLike(通配符匹配),通配符方式需确保不会包含无关规则,不如精确匹配稳健

内容的提问来源于stack exchange,提问作者allan.simon

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.21 17:10:04