【问题标题】:Terraform: configuring cloudwatch log subscription delivery to lambda?Terraform:将 cloudwatch 日志订阅交付配置为 lambda?
【发布时间】:2016-11-19 08:17:54
【问题描述】:

我需要将我的 cloudwatch 日志发送到日志分析服务。

我已经按照herehere 的这些文章进行了操作,并且可以手动操作,不用担心。

现在我正在尝试使用 Terraform 自动化所有这些(角色/策略、安全组、cloudwatch 日志组、lambda 以及从日志组触发 lambda)。

但我不知道如何使用 TF 来配置 AWS 以从 cloudwatch 日志中触发 lambda。

我可以通过执行以下操作(在 Lambda Web 控制台 UI 中)手动将两个 TF 资源链接在一起:

  • 进入 lambda 函数的“触发器”部分
  • 点击“添加触发器”
  • 从触发器类型列表中选择“cloudwatch 日志”
  • 选择我要触发 lambda 的日志组
  • 输入过滤器名称
  • 将过滤模式留空(暗示在所有日志流上触发)
  • 确保选中“启用触发器”
  • 点击提交按钮

完成后,lambda 将显示在 cloudwatch 日志控制台的订阅列中 - 显示为“Lambda (cloudwatch-sumologic-lambda)”。

我尝试使用以下 TF 资源创建订阅:

resource "aws_cloudwatch_log_subscription_filter" "cloudwatch-sumologic-lambda-subscription" {
  name            = "cloudwatch-sumologic-lambda-subscription"
  role_arn        = "${aws_iam_role.jordi-waf-cloudwatch-lambda-role.arn}"
  log_group_name  = "${aws_cloudwatch_log_group.jordi-waf-int-app-loggroup.name}"
  filter_pattern  = "logtype test"
  destination_arn = "${aws_lambda_function.cloudwatch-sumologic-lambda.arn}"
}

但它失败了:

aws_cloudwatch_log_subscription_filter.cloudwatch-sumologic-lambda-subscription:InvalidParameterException:vendor lambda 的destinationArn 不能与roleArn 一起使用

我发现this answer 为计划的事件设置类似的东西,但这似乎不等同于我上面描述的控制台操作(控制台 UI 方法不会创建事件/规则我可以看到)。

谁能给我指点一下我做错了什么?

【问题讨论】:

  • 很难确切地看到,但 AWS 似乎在说您赋予 PutSubscriptionFilter 的角色无权访问 Lambda。能否也发布aws_iam_role.jordi-waf-cloudwatch-lambda-role.arn资源的定义?
  • 只要确保您的destination_arnaws_cloudwatch_log_subscription_filter 资源最后不包含类似“:*”的内容。我搞砸了,花了一整天的时间解决这个问题。

标签: amazon-web-services terraform sumologic


【解决方案1】:

我的 aws_cloudwatch_log_subscription_filter 资源定义不正确 - 在这种情况下您不应该提供 role_arn 参数。

您还需要添加一个aws_lambda_permission 资源(在过滤器上定义depends_on 关系,否则TF 可能会以错误的顺序执行此操作)。

请注意,AWS lambda 控制台 UI 会不可见地为您添加 lambda 权限,因此请注意,如果您之前在控制台 UI 中碰巧做过相同的操作,aws_cloudwatch_log_subscription_filter 将在没有权限资源的情况下工作。

必要的 TF 配置如下所示(最后两个资源是配置实际 cloudwatch->lambda 触发器的相关资源):

// intended for application logs (access logs, modsec, etc.)
resource "aws_cloudwatch_log_group" "test-app-loggroup" {
  name              = "test-app"
  retention_in_days = 90
}

resource "aws_security_group" "cloudwatch-sumologic-lambda-sg" {
  name = "cloudwatch-sumologic-lambda-sg"

  tags {
    Name = "cloudwatch-sumologic-lambda-sg"
  }

  description = "Security group for lambda to move logs from CWL to SumoLogic"
  vpc_id      = "${aws_vpc.dev-vpc.id}"
}

resource "aws_security_group_rule" "https-egress-cloudwatch-sumologic-to-internet" {
  type              = "egress"
  from_port         = 443
  to_port           = 443
  protocol          = "tcp"
  security_group_id = "${aws_security_group.cloudwatch-sumologic-lambda-sg.id}"
  cidr_blocks       = ["0.0.0.0/0"]
}

resource "aws_iam_role" "test-cloudwatch-lambda-role" {
  name = "test-cloudwatch-lambda-role"

  assume_role_policy = <<EOF
{
  "Version": "2012-10-17",
  "Statement": [
    {
      "Action": "sts:AssumeRole",
      "Principal": {
        "Service": "lambda.amazonaws.com"
      },
      "Effect": "Allow"
    }
  ]
}
EOF
}

resource "aws_iam_role_policy" "test-cloudwatch-lambda-policy" {
  name = "test-cloudwatch-lambda-policy"
  role = "${aws_iam_role.test-cloudwatch-lambda-role.id}"

  policy = <<EOF
{
  "Version": "2012-10-17",
  "Statement": [
    {
      "Sid": "CopiedFromTemplateAWSLambdaVPCAccessExecutionRole1",
      "Effect": "Allow",
      "Action": [
        "ec2:CreateNetworkInterface"
      ],
      "Resource": "*"
    },
    {
      "Sid": "CopiedFromTemplateAWSLambdaVPCAccessExecutionRole2",
      "Effect": "Allow",
      "Action": [
        "ec2:DescribeNetworkInterfaces",
        "ec2:DeleteNetworkInterface"
      ],
      "Resource": "arn:aws:ec2:ap-southeast-2:${var.dev_vpc_account_id}:network-interface/*"
    },

    {
      "Sid": "CopiedFromTemplateAWSLambdaBasicExecutionRole1",
      "Effect": "Allow",
      "Action": "logs:CreateLogGroup",
      "Resource": "arn:aws:logs:ap-southeast-2:${var.dev_vpc_account_id}:*"
    },
    {
      "Sid": "CopiedFromTemplateAWSLambdaBasicExecutionRole2",
      "Effect": "Allow",
      "Action": [
        "logs:CreateLogStream",
        "logs:PutLogEvents"
      ],
      "Resource": [
    "arn:aws:logs:ap-southeast-2:${var.dev_vpc_account_id}:log-group:/aws/lambda/*"
      ]
    },

    {
      "Sid": "CopiedFromTemplateAWSLambdaAMIExecutionRole",
      "Effect": "Allow",
      "Action": [
         "ec2:DescribeImages"
      ],
      "Resource": "*"
    }


  ]
}
EOF
}

resource "aws_lambda_function" "cloudwatch-sumologic-lambda" {
  function_name    = "cloudwatch-sumologic-lambda"
  filename         = "${var.lambda_dir}/cloudwatchSumologicLambda.zip"
  source_code_hash = "${base64sha256(file("${var.lambda_dir}/cloudwatchSumologicLambda.zip"))}"
  handler          = "cloudwatchSumologic.handler"

  role        = "${aws_iam_role.test-cloudwatch-lambda-role.arn}"
  memory_size = "128"
  runtime     = "nodejs4.3"

  // set low because I'm concerned about cost-blowout in the case of mis-configuration
  timeout = "15"

  vpc_config = {
    subnet_ids         = ["${aws_subnet.dev-private-subnet.id}"]
    security_group_ids = ["${aws_security_group.cloudwatch-sumologic-lambda-sg.id}"]
  }
}

resource "aws_lambda_permission" "test-app-allow-cloudwatch" {
  statement_id  = "test-app-allow-cloudwatch"
  action        = "lambda:InvokeFunction"
  function_name = "${aws_lambda_function.cloudwatch-sumologic-lambda.arn}"
  principal     = "logs.ap-southeast-2.amazonaws.com"
  source_arn    = "${aws_cloudwatch_log_group.test-app-loggroup.arn}"
}

resource "aws_cloudwatch_log_subscription_filter" "test-app-cloudwatch-sumologic-lambda-subscription" {
  depends_on      = ["aws_lambda_permission.test-app-allow-cloudwatch"]
  name            = "cloudwatch-sumologic-lambda-subscription"
  log_group_name  = "${aws_cloudwatch_log_group.test-app-loggroup.name}"
  filter_pattern  = ""
  destination_arn = "${aws_lambda_function.cloudwatch-sumologic-lambda.arn}"
}

编辑:请注意,上面的 TF 代码是几年前编写的,使用版本 0.11.x - 它应该仍然可以工作,但可能有更好的处理方式。具体来说,除非需要,否则不要使用这样的内联策略,而是使用 aws_iam_policy_document - 随着时间的推移,它们更容易维护。

【讨论】:

  • 我认为没有必要创建“aws_cloudwatch_log_group”,因为它已经由 lambda 函数在推送日志时创建。如果你想推动一些特定的场景,那么也许没问题。
  • @JayeshDhandha 从 TF 创建资源(日志组、权限等)总是比让 AWS 后端为您创建资源要好。这样,当您想正确配置它们时(在 log_group 的情况下,例如当您想控制争用时),您就可以处理它们。此外,通过自己创建它们,您可以准确了解事物的配置方式(自动创建的 AWS 资源未记录在案,可能会发生变化)。
  • 很好的解决方案,感谢分享,但我必须在日志组 arn 的末尾添加“”:${aws_cloudwatch_log_group.test-app-loggroup.arn}:
  • 我希望这个例子出现在这个页面上:terraform.io/docs/providers/aws/r/…
  • 我必须按照下面 RtmY 的建议设置source_arn = "${aws_cloudwatch_log_group.test-app-loggroup.arn}:*" 。我使用的是 terraform 0.14.6
【解决方案2】:

使用 Terraform v0.12.29 和 AWS 提供商 v3.1.0 我遇到了一个奇怪的问题,我花了几个小时进行调试。

为了节省其他人宝贵的时间,我将分享它作为对已接受答案的补充。

cloudwatch日志组arn的值

${aws_cloudwatch_log_group.test-app-loggroup.arn}

未正确插值 - 输出末尾缺少“:*”。

这会导致以下错误:

创建 {the-calling-service} 时出错:InvalidCloudWatchLogsLogGroupArnException: 检查日志组 ARN:{the-calling-service} 无法验证它。

添加:* 后缀解决了这个问题:

source_arn = "${aws_cloudwatch_log_group.test-app-loggroup.arn}:*" #<----Notice the :* postfix

【讨论】:

  • 我不再发送我的日志(现在使用日志洞察)。我回到了我的 git 历史并查看了 source_arn 以获取我删除时的 lambda 权限。它只是 arn(没有 ":*"),虽然它没有使用字符串插值(因为之前升级到 TF 0.12.x)。
  • 您能否将其 (TF arn) 与您在 AWS 控制台中看到的 arn 值进行比较?
  • 谢谢!我自己发现了这个问题,然后去寻找确认。
猜你喜欢
  • 2020-05-13
  • 1970-01-01
  • 2021-09-05
  • 2019-10-01
  • 1970-01-01
  • 2021-03-16
  • 1970-01-01
  • 2020-06-25
  • 2021-04-20
相关资源
最近更新 更多