Skip to main content

============================================================================

Slack Notification Lambda for CloudWatch Alarms

============================================================================

# Local variables
locals {
  slack_webhook_url    = "https://hooks.slack.com/services/xx"
  lambda_function_name = "CloudWatch-Alarms-To-Slack"
  lambda_handler       = "lambda_function.lambda_handler"
  lambda_runtime       = "python3.11"
}

# ============================================================================
# SNS Topic for Slack Notifications
# ============================================================================

# This uses the existing SNS topic from variables.tf
# No need to create a new one, we'll just add Lambda as a subscriber

# ============================================================================
# IAM Role for Lambda Function
# ============================================================================


resource "aws_iam_role" "lambda_slack_role" {
  name = "${local.lambda_function_name}-Role"

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

  tags = {
    Name        = "${local.lambda_function_name}-Role"
    Application = "CommonInfraResource"
    Environment = "Production"
    ManagedBy   = "Terraform"
  }
}

# Attach basic Lambda execution policy
resource "aws_iam_role_policy_attachment" "lambda_basic_execution" {
  role       = aws_iam_role.lambda_slack_role.name
  policy_arn = "arn:aws:iam::aws:policy/service-role/AWSLambdaBasicExecutionRole"
}

# ============================================================================
# CloudWatch Log Group for Lambda
# ============================================================================

resource "aws_cloudwatch_log_group" "lambda_log_group" {
  name              = "/aws/lambda/${local.lambda_function_name}"
  retention_in_days = 14

  tags = {
    Name        = "${local.lambda_function_name}-LogGroup"
    Application = "CommonInfraResource"
    Environment = "Production"
    ManagedBy   = "Terraform"
  }
}

# ============================================================================
# Lambda Function
# ============================================================================

# Create ZIP file from Python code
data "archive_file" "lambda_zip" {
  type        = "zip"
  source_file = "${path.module}/lambda-code/lambda_function.py"
  output_path = "${path.module}/lambda-code/lambda_function.zip"
}

resource "aws_lambda_function" "slack_notifier" {
  filename         = data.archive_file.lambda_zip.output_path
  function_name    = local.lambda_function_name
  role             = aws_iam_role.lambda_slack_role.arn
  handler          = local.lambda_handler
  source_code_hash = data.archive_file.lambda_zip.output_base64sha256
  runtime          = local.lambda_runtime
  timeout          = 30
  memory_size      = 256

  environment {
    variables = {
      SLACK_WEBHOOK_URL = local.slack_webhook_url
    }
  }

  tags = {
    Name        = local.lambda_function_name
    Application = "CommonInfraResource"
    Environment = "Production"
    ManagedBy   = "Terraform"
  }

  depends_on = [
    aws_cloudwatch_log_group.lambda_log_group,
    aws_iam_role_policy_attachment.lambda_basic_execution
  ]
}

# ============================================================================
# SNS Topic Subscription - Lambda
# ============================================================================

resource "aws_sns_topic_subscription" "lambda_subscription" {
  topic_arn = var.sns_topic_arn
  protocol  = "lambda"
  endpoint  = aws_lambda_function.slack_notifier.arn
}

# Give SNS permission to invoke Lambda
resource "aws_lambda_permission" "allow_sns" {
  statement_id  = "AllowExecutionFromSNS"
  action        = "lambda:InvokeFunction"
  function_name = aws_lambda_function.slack_notifier.function_name
  principal     = "sns.amazonaws.com"
  source_arn    = var.sns_topic_arn
}

# ============================================================================
# Test Lambda Function (Optional - can be run manually)
# ============================================================================

# This creates a test event to validate the Lambda function works
resource "null_resource" "test_lambda" {
  # Only run this when Lambda is created or updated
  triggers = {
    lambda_arn = aws_lambda_function.slack_notifier.arn
  }

  provisioner "local-exec" {
    command = <<-EOT
      echo "Testing Lambda function...."
      aws lambda invoke \
        --function-name ${aws_lambda_function.slack_notifier.function_name} \
        --cli-binary-format raw-in-base64-out \
        --payload file://${path.module}/lambda-code/test-event.json \
        --region ap-south-1 \
        /tmp/lambda-test-output.json
      
      echo "TF - Lambda test output:"
      cat /tmp/lambda-test-output.json
      echo ""
    EOT
  }

  depends_on = [
    aws_lambda_function.slack_notifier,
    aws_sns_topic_subscription.lambda_subscription,
    aws_lambda_permission.allow_sns
  ]
}