Skip to content

guidetech/function-gt-notify-discord

Repository files navigation

Function Notify Discord

Lambda Function que recebe eventos de pipelines AWS CodePipeline via EventBridge e envia notificações formatadas para webhooks do Discord.

Visão Geral

Esta função é agnóstica a pipelines específicos. O webhook Discord é injetado no evento pelo EventBridge via Input Transformer configurado no Terraform de cada pipeline.

Arquitetura

CodePipeline → EventBridge (injeta webhook_url) → Lambda → Discord Webhook

Fluxo de Dados

  1. Pipeline do CodePipeline muda de estado (SUCCEEDED, FAILED, etc)
  2. EventBridge captura o evento
  3. Input Transformer injeta webhook_url no evento (configurado via Terraform)
  4. Lambda recebe evento, formata mensagem e envia para Discord
  5. Discord exibe notificação com embed colorido

Estrutura do Evento

A Lambda espera eventos do EventBridge com a seguinte estrutura:

{
  "detail": {
    "pipeline": "nome-do-pipeline",
    "state": "SUCCEEDED",
    "execution-id": "abc-123-xyz",
    "webhook_url": "https://discord.com/api/webhooks/..."
  },
  "time": "2025-11-18T12:00:00Z"
}

Atributo webhook_url: Injetado pelo Terraform via EventBridge Input Transformer.

Configuração no Terraform

Exemplo de EventBridge Rule com Input Transformer

variable "discord_webhook_url" {
  description = "Discord webhook URL for pipeline notifications"
  type        = string
  sensitive   = true
}

variable "pipeline_name" {
  description = "Nome do pipeline CodePipeline"
  type        = string
}

variable "discord_lambda_arn" {
  description = "ARN da Lambda function-notify-discord"
  type        = string
}

# Regra do EventBridge
resource "aws_cloudwatch_event_rule" "pipeline_notifications" {
  name        = "discord-notify-${var.pipeline_name}"
  description = "Notifica Discord sobre mudanças no pipeline ${var.pipeline_name}"

  event_pattern = jsonencode({
    source      = ["aws.codepipeline"]
    detail-type = ["CodePipeline Pipeline Execution State Change"]
    detail = {
      pipeline = [var.pipeline_name]
    }
  })
}

# Target com Input Transformer - INJETA O WEBHOOK
resource "aws_cloudwatch_event_target" "discord_lambda" {
  rule = aws_cloudwatch_event_rule.pipeline_notifications.name
  arn  = var.discord_lambda_arn

  input_transformer {
    input_paths = {
      pipeline     = "$.detail.pipeline"
      state        = "$.detail.state"
      execution_id = "$.detail.execution-id"
      time         = "$.time"
      region       = "$.region"
      account      = "$.account"
    }

    input_template = <<-JSON
    {
      "detail": {
        "pipeline": <pipeline>,
        "state": <state>,
        "execution-id": <execution_id>,
        "webhook_url": "${var.discord_webhook_url}",
        "region": <region>,
        "account": <account>
      },
      "time": <time>
    }
    JSON
  }
}

# Permissão para EventBridge invocar Lambda
resource "aws_lambda_permission" "allow_eventbridge" {
  statement_id  = "AllowExecutionFromEventBridge-${var.pipeline_name}"
  action        = "lambda:InvokeFunction"
  function_name = var.discord_lambda_arn
  principal     = "events.amazonaws.com"
  source_arn    = aws_cloudwatch_event_rule.pipeline_notifications.arn
}

Desenvolvimento

Instalação de Dependências

pip install -r requirements.txt

Estrutura do Projeto

.
├── lambda_function.py    # Handler da Lambda
├── discord_notify.py     # Lógica de notificação Discord
├── logger.py            # Sistema de logging estruturado
├── requirements.txt     # Dependências Python
├── event-test.json      # Evento mock para testes
├── CLAUDE.md           # Documentação para Claude Code
└── README.md           # Este arquivo

Build e Deploy

O empacotamento é feito automaticamente pelo AWS CodeBuild via buildspec.yml:

# buildspec.yml
build:
  commands:
    - pip install -r requirements.txt -t ./package
    - cp lambda_function.py discord_notify.py logger.py ./package/
    - cd package && zip -r ../function.zip . && cd ..

O que o build faz:

  1. Instala dependências Python (requests) na pasta ./package
  2. Copia código da aplicação para ./package
  3. Cria function.zip com código + dependências
  4. Artefato disponibilizado para deploy

Arquivos gerados (não versionados no Git):

  • function.zip - Pacote final
  • package/ - Pasta temporária de build

Deploy manual (desenvolvimento):

# Instalar dependências
pip install -r requirements.txt -t ./package

# Copiar código
cp lambda_function.py discord_notify.py logger.py ./package/

# Criar pacote
cd package && zip -r ../function.zip . && cd ..

# Deploy via AWS CLI
aws lambda update-function-code \
  --function-name function-notify-discord \
  --zip-file fileb://function.zip

# Limpar
rm -rf package/ function.zip

Variáveis de Ambiente

A Lambda requer apenas uma variável de ambiente:

Variável Descrição Obrigatório
WEBHOOK_TEST Webhook Discord de fallback caso webhook_url não venha no evento Sim

Formato das Mensagens Discord

As mensagens são enviadas como embeds Discord com cores e emojis baseados no estado do pipeline:

Cores e Emojis por Estado

Estado Cor Emoji Código
SUCCEEDED Verde 1673044
FAILED Vermelho 16724787
STARTED Azul 🚀 3447003
STOPPED Laranja ⚠️ 15258703
SUPERSEDED Cinza 🔄 9807270
Outros Laranja ℹ️ 15258703

Exemplo de Mensagem

{
  "content": "✅ Pipeline succeeded",
  "embeds": [{
    "title": "ppl-gt-api-perito-prd",
    "description": "Estado do pipeline alterado para **SUCCEEDED**",
    "color": 1673044,
    "fields": [
      {
        "name": "ID da Execução",
        "value": "`abc-123-xyz`",
        "inline": false
      },
      {
        "name": "Status",
        "value": "**SUCCEEDED**",
        "inline": true
      }
    ],
    "footer": {
      "text": "AWS CodePipeline Notification"
    },
    "timestamp": "2025-11-18T12:00:00Z"
  }]
}

Testes

Evento Mock para Teste Local

Um arquivo de teste está disponível em event-test.json:

{
  "detail": {
    "pipeline": "ppl-teste",
    "state": "SUCCEEDED",
    "execution-id": "test-123-abc-xyz",
    "webhook_url": "https://discord.com/api/webhooks/SEU_WEBHOOK_AQUI",
    "region": "us-east-1",
    "account": "123456789012"
  },
  "time": "2025-11-18T12:00:00Z"
}

Teste Local via Python

import json
from lambda_function import lambda_handler

class MockContext:
    aws_request_id = "test-request-id-123"
    function_name = "function-notify-discord"
    function_version = "$LATEST"
    memory_limit_in_mb = 128

    def get_remaining_time_in_millis(self):
        return 300000

with open('event-test.json') as f:
    event = json.load(f)

lambda_handler(event, MockContext())

Teste via AWS CLI

Você pode testar a Lambda diretamente na AWS usando o arquivo de teste:

# Edite event-test.json e adicione seu webhook Discord
# Depois invoque a Lambda:
aws lambda invoke \
  --function-name function-notify-discord \
  --payload file://event-test.json \
  --cli-binary-format raw-in-base64-out \
  response.json

# Ver resposta
cat response.json

Testar Diferentes Estados

Você pode modificar o campo state no event-test.json para testar diferentes cores:

  • "state": "SUCCEEDED" - Mensagem verde
  • "state": "FAILED" - Mensagem vermelha
  • "state": "STARTED" - Mensagem laranja

Sistema de Logging

A Lambda utiliza logging estruturado em JSON otimizado para CloudWatch Logs Insights.

Características

  • Formato JSON para todos os logs
  • lambda_request_id automático em todos os logs
  • Timestamps UTC no formato ISO 8601
  • Stack traces completos em exceções
  • Contexto estruturado via extra={}
  • Redução de ruído (boto3, urllib3, requests em nível WARNING)
  • Métricas de performance (tempo de execução)

Exemplo de Log

{
  "timestamp": "2025-11-18T12:34:56.789Z",
  "level": "INFO",
  "logger": "discord_notify",
  "message": "Pipeline identificado",
  "module": "discord_notify",
  "function": "parse_event",
  "line": 35,
  "lambda_request_id": "abc-123-def-456",
  "pipeline": "ppl-gt-api-perito-prd",
  "execution_id": "exec-123",
  "state": "SUCCEEDED"
}

Logs Gerados

Início da execução:

{
  "message": "Lambda invocada",
  "function_name": "function-notify-discord",
  "function_version": "$LATEST",
  "request_id": "abc-123-def-456",
  "memory_limit_mb": 128,
  "remaining_time_ms": 299000
}

Processamento:

{
  "message": "Pipeline identificado",
  "pipeline": "ppl-gt-api-perito-prd",
  "execution_id": "exec-abc-123",
  "state": "SUCCEEDED"
}

Sucesso:

{
  "message": "Notificação enviada com sucesso",
  "status_code": 204,
  "execution_time_seconds": 0.45,
  "pipeline": "ppl-gt-api-perito-prd",
  "state": "SUCCEEDED"
}

Erro:

{
  "level": "ERROR",
  "message": "Erro ao enviar notificação Discord: Connection timeout",
  "error_type": "RequestException",
  "exception": "Traceback...",
  "pipeline": "ppl-gt-api-perito-prd",
  "state": "FAILED"
}

CloudWatch Logs Insights

Filtrar por pipeline:

fields @timestamp, message, state, execution_time_seconds
| filter pipeline = "ppl-gt-api-perito-prd"
| sort @timestamp desc

Analisar erros:

fields @timestamp, message, error_type, exception
| filter level = "ERROR"
| stats count() by error_type

Métricas de performance:

fields @timestamp, execution_time_seconds, pipeline, state
| filter ispresent(execution_time_seconds)
| stats avg(execution_time_seconds), max(execution_time_seconds) by pipeline

Rastrear execução específica:

fields @timestamp, message, function, line
| filter lambda_request_id = "abc-123-def-456"
| sort @timestamp asc

Vantagens da Arquitetura

  • Lambda agnóstica - não conhece pipelines específicos
  • Escalável - adicionar pipeline = só Terraform, zero mudança na Lambda
  • Manutenível - webhook gerenciado no Terraform junto com o pipeline
  • Testável - evento mock simples para testes
  • Sem hardcode - nenhuma lógica condicional baseada em nomes de pipeline

Troubleshooting

Lambda não recebe eventos

  1. Verificar EventBridge Rule está ativa
  2. Verificar permissão aws_lambda_permission está configurada
  3. Verificar logs do CloudWatch da Lambda

Mensagem não aparece no Discord

  1. Verificar webhook_url está correto no evento (logs da Lambda)
  2. Testar webhook manualmente com curl:
    curl -X POST "WEBHOOK_URL" \
      -H "Content-Type: application/json" \
      -d '{"content": "Teste"}'
  3. Verificar webhook não foi deletado/revogado no Discord

Fallback para WEBHOOK_TEST sendo usado

  1. Verificar Input Transformer do EventBridge está injetando webhook_url
  2. Verificar logs da Lambda para ver estrutura do evento recebido
  3. Conferir que webhook_url está sendo passado corretamente no input_template

Licença

Projeto interno GuideTech.

About

No description, website, or topics provided.

Resources

Stars

Watchers

Forks

Releases

No releases published

Packages

No packages published

Languages