Resource Not Found - Lambda permission

Hi

I have a Terraform Cloud with VCS project, with the following structure:

.
├── modules
│   ├── event
│   │   ├── main.tf
│   │   ├── outputs.tf
│   │   └── variables.tf
│   └── lambda
│       ├── main.tf
│       ├── outputs.tf
│       └── variables.tf
└── staging
    ├── backend.tf
    ├── main.tf
    ├── src
    │   ├── node.zip
    │   └── python.zip
    └── variables.tf

Files content:

modules/event/main.tf

data "aws_caller_identity" "current" {}

locals {

  lambda_arn = "arn:aws:lambda:${var.region}:${data.aws_caller_identity.current.account_id}:function:${var.lambda_name}"
  
}

resource "aws_cloudwatch_event_rule" "rule" {
  name = "${var.lambda_name}-SR" 
  schedule_expression = var.rate
}

resource "aws_cloudwatch_event_target" "target" {
  rule = aws_cloudwatch_event_rule.rule.name
  target_id = var.lambda_name
  arn    = local.lambda_arn
}

resource "aws_lambda_permission" "permission" {
  statement_id = "AllowExecutionFromCloudWatch"
  action = "lambda:InvokeFunction"
  function_name = var.lambda_name
  principal = "events.amazonaws.com"
  source_arn = aws_cloudwatch_event_rule.rule.arn
  }

modules/event/output.tf

output "event_arn" {
  value = aws_cloudwatch_event_rule.rule.arn
}

modules/event/variables.tf

variable "lambda_name" {
  type = string
}

variable "region" {
  type = string
}

variable "rate" {
  type = string
}

modules/lambda/main.tf

data "aws_caller_identity" "current" {}



resource "aws_lambda_function" "func" {

  tags = {
    STAGE = var.staging
  }

  tags_all = {
    STAGE = var.staging
  }

  architectures = ["x86_64"]

  environment {
    variables = merge(var.envvars, {
      ACCOUNT_ID                             = data.aws_caller_identity.current.account_id
      API_URL                                = "https://api.example.com/api/v3"
      DOCUMENTS_LIMIT                        = 100
      CLIENT_DOMAIN                          = "https://staging.example.com"
      DATABASE_NAME                          = "example_db"
      DEFAULT_REDIS_PORT                     = "6379"
      DOMAIN                                 = "https://example.com/"
      LANGUAGE_DETECT_API_KEY                = "API-sS03sdvlqacad0032343td93kxka2a"
      NODE_ENV                               = "staging"
      S3_BUCKET                              = "staging-bucket"
      S3_REGION                              = "us-east-2"
      SSM_ACCESS_KEY_ID                      = var.SSM_ACCESS_KEY_ID
      SSM_SECRET_ACCESS_KEY                  = var.SSM_SECRET_ACCESS_KEY
    })
  }

  ephemeral_storage {
    size = 512
  }

  function_name                  = replace(var.name, "_", "-")
  filename                       = "./src/node.zip" 
  handler                        = var.handler
  memory_size                    = var.memory_size
  package_type                   = "Zip"
  reserved_concurrent_executions = -1
  role                           = aws_iam_role.role.arn
  runtime                        = var.runtime
  source_code_hash               = var.source_code_hash
  timeout                        = var.timeout
  tracing_config {
    mode = "PassThrough"
  }

  vpc_config {
    security_group_ids = [var.security_group_id]
    subnet_ids = var.subnet_ids
  }

}


resource "aws_iam_policy" "policy" {

  description = "Policy for ${var.name} Lambda function"

  policy = jsonencode({
    Version = "2012-10-17"
    Statement = [
      {
        Action = [
          "ssm:Describe*",
          "ssm:Get*",
          "ssm:List*"
        ],
        Resource = "*",
        Effect = "Allow"
      },
      {
        Action = [
          "s3:GetObject"
        ],
        Resource = "arn:aws:s3:::stg-stripe-receipts/*"
        Effect = "Allow"
      },
      {
        Action = [
          "s3:PutObject"
        ],
        Resource = "arn:aws:s3:::stg-stripe-receipts/*"
        Effect = "Allow"
      },
      {
        Action = [
          "sqs:ReceiveMessage",
          "sqs:DeleteMessage",
          "sqs:GetQueueAttributes"
        ],
        Resource = "*"
        Effect = "Allow"
      }
    ]
  })
}


resource "aws_iam_role" "role" {

  tags = {
    STAGE = var.staging
  }

  tags_all = {
    STAGE = var.staging
  }

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

resource "aws_iam_role_policy_attachment" "custom_policy" {
  role       = aws_iam_role.role.name
  policy_arn = aws_iam_policy.policy.arn
}

modules/lambda/output.tf

output "lambda_function_name" {
  description = "Name of the Lambda function"
  value = aws_lambda_function.func.function_name
}

modules/lambda/variable.tf

variable name {
  type = string
}

variable handler {
  type = string
}

variable memory_size {
  type = number
}

variable runtime {
  type = string
}

variable source_code_hash {
  type = string
}

variable timeout {
  type = number
}

variable event {
  type = string
  default = ""
}

variable envvars {
  type = map(string)
}

variable SSM_ACCESS_KEY_ID {
  type = string
}

variable SSM_SECRET_ACCESS_KEY {
  type = string
}

variable stage {
  type = string
}

staging/main.tf

module "event" {

  source = "../modules/event"

  for_each = tomap({for k,v in var.lambdas : k => v if length(v.event) > 0})

  lambda_name = each.key
  region = var.region
  rate = each.value.event.rate
}


module "lambda_event" {
  source = "../modules/lambda"

  for_each = tomap({for k,v in var.lambdas : k => v if length(v.event) > 0})

  name = each.key
  handler = each.value.handler
  memory_size = each.value.memory_size
  runtime = each.value.runtime
  source_code_hash = each.value.source_code_hash
  timeout = each.value.timeout
  envvars = each.value.envvars
  SSM_ACCESS_KEY_ID = var.SSM_ACCESS_KEY_ID
  SSM_SECRET_ACCESS_KEY = var.SSM_SECRET_ACCESS_KEY
  stage = var.environment
  security_group_id = module.m_vpc.security_group_id_project
  subnet_ids = [
    module.m_vpc.private_subnet_1,
    module.m_vpc.private_subnet_2
  ]
  event = module.m_event[each.key].event_arn
}

staging/backend.tf

provider "aws" {
  region  = var.region
}

terraform {
  cloud {
    organization = "infrastructure"
    workspaces {
      name = "staging"
    }
  }

  required_providers {
    aws = {
      source  = "hashicorp/aws"
      version = "~> 5.82.2" 
    }
  }
}

staging/main.tf

variable "lambdas" {
  default = {

  lambda01 = {
      handler = "handlers/lambda01.handler"
      memory_size = 512
      runtime = "nodejs16.x"
      source_code_hash = "YYYYYYYYY"
      timeout = 900
      sqs = {}
      event = {
        rate = "rate(5 minutes)"
      }
      envvars = {
        VAR1 = "VAL1"
        VAR2 = "VAL2"
      }
    }

  lambda02 = {
      handler = "handlers/lambda02.handler"
      memory_size = 512
      runtime = "nodejs16.x"
      source_code_hash = "YYYYYYYYY"
      timeout = 900
      sqs = {}
      event = {
        rate = "rate(5 minutes)"
      }
      envvars = {
        VAR1 = "VAL1"
        VAR2 = "VAL2"
      }
    }

  }
}

When running the project I am getting the following error message

Error: adding Lambda Permission (LambdaFunction01/AllowExecutionFromCloudWatch): operation error Lambda: AddPermission, https response error StatusCode:
404, RequestID: 8b29-z12a-t12855zc2b9, ResourceNotFoundException: Function not found: arn:aws:lambda:us-east-2:1111111111:function:LambdaFunction01
with module.event["LambdaFunction01"].aws_lambda_permission.permission
on ../modules/event/main.tf line 21, in resource "aws_lambda_permission" "permission":
resource "aws_lambda_permission" "permission" {

Does anyone have any idea what the problem is and how to fix it?

Thanks

P.S. There are some other modules/files, such as VPC and Security Group creation . However, I don’t think they have anything to do with the problem

What looks like it is happening is that your events module and lambdas module do not have a dependency attachment.

So, your aws_lambda_permission for your event bridge target is being created before your lambda is created. That is why it cannot find the lambda name.

You need to either move the aws_lambda_permission to your lambda module and use the event variable you have, or you need remove the event variable from your lambda module, and pass the lambda name to your event module from your lambda module.