
Aqui está o material de apoio com os códigos e demostrações de como concluir o minicurso de Python para DevOps na AWS
Table of contents
Open Table of contents
- Pré-requisitos
- CloudWatch Logs
- Criação do Metric Filter
- Criação do Alarme
- Criação do EventBridge Rule
- Exemplos de Payload
- Base do nosso State Machine
- Setup Python
- Credenciais
- Integração Strands Agents com Groq Cloud
- Integração com o Discord
- Armazenar suas credenciais de forma segura
- Criando uma Layer para a Lambda
- Código Python da Lambda Multiagente
- Código Python da Lambda do Discord Webhook
- JSONata do PutObject S3
- Comandos para testar o Alarme
Pré-requisitos
- Estar cadastrado no AWS Academy no Learner Labs
- Python 3.9+ instalado
- Conta na Groq Cloud
- Conta no Discord
CloudWatch Logs
- Aqui está alguns comandos prontos para testar nossa automação
- Antes de tudo, crie o seguinte Log Group, vamos centralizar todos os Log Streams nele
- Dessa forma, configuramos nosso Metric Filter que vai se aplicar a todos os logs
aws logs create-log-group \
--log-group-name /minicurso/cloud-incident
Importante: Todos os Log Streams serão criados dentro deste único Log Group, facilitando a configuração do Metric Filter.
Criação do Metric Filter
aws logs put-metric-filter \
--log-group-name /minicurso/cloud-incident \
--filter-name "ErrorDetection" \
--filter-pattern "ERROR" \
--metric-transformations "metricName=ErrorCount,metricNamespace=MinicursoIncidents,metricValue=1,defaultValue=0"
Criação do Alarme
aws cloudwatch put-metric-alarm \
--alarm-name "IncidentDetected" \
--alarm-description "Dispara quando ERROR é detectado nos logs" \
--metric-name ErrorCount \
--namespace MinicursoIncidents \
--statistic Sum \
--period 60 \
--evaluation-periods 1 \
--threshold 1 \
--comparison-operator GreaterThanOrEqualToThreshold
Criação do EventBridge Rule
aws events put-rule \
--name "DispararStepFunctionDeIncidentes" \
--event-pattern '{
"source": ["aws.cloudwatch"],
"detail-type": ["CloudWatch Alarm State Change"],
"detail": {
"alarmName": ["IncidentDetected"],
"state": {
"value": ["ALARM"]
}
}
}' \
--description "Aciona o Step Function de incidentes quando o alarme do CloudWatch dispara"
Exemplos de Payload
- CloudWatch Alarm
{
"version": "0",
"id": "8bb5a117-e497-cc0b-8d78-0e918d33d409",
"detail-type": "CloudWatch Alarm State Change",
"source": "aws.cloudwatch",
"account": "905418102592",
"time": "2025-10-01T01:54:12Z",
"region": "us-east-1",
"resources": [
"arn:aws:cloudwatch:us-east-1:905418102592:alarm:Incident Metric Alarm"
],
"detail": {
"alarmName": "Incident Metric Alarm",
"state": {
"value": "ALARM",
"reason": "Threshold Crossed: 1 out of the last 1 datapoints [2.0 (01/10/25 01:53:00)] was greater than or equal to the threshold (1.0) (minimum 1 datapoint for OK -> ALARM transition).",
"reasonData": "{\"version\":\"1.0\",\"queryDate\":\"2025-10-01T01:54:12.303+0000\",\"startDate\":\"2025-10-01T01:53:00.000+0000\",\"statistic\":\"Sum\",\"period\":60,\"recentDatapoints\":[2.0],\"threshold\":1.0,\"evaluatedDatapoints\":[{\"timestamp\":\"2025-10-01T01:53:00.000+0000\",\"sampleCount\":2.0,\"value\":2.0}]}",
"timestamp": "2025-10-01T01:54:12.304+0000"
},
"previousState": {
"value": "INSUFFICIENT_DATA",
"reason": "Unchecked: Initial alarm creation",
"timestamp": "2025-10-01T01:52:11.127+0000"
},
"configuration": {
"metrics": [
{
"id": "44cccebc-26bb-b9ff-92f4-87b2e87eb077",
"metricStat": {
"metric": {
"namespace": "IncidentMonitoring",
"name": "ErrorCount",
"dimensions": {}
},
"period": 60,
"stat": "Sum"
},
"returnData": true
}
]
}
}
}
- Metric Filter
{
"metricFilters": [
{
"applyOnTransformedLogs": boolean,
"creationTime": number,
"emitSystemFieldDimensions": [ "string" ],
"fieldSelectionCriteria": "string",
"filterName": "string",
"filterPattern": "string",
"logGroupName": "string",
"metricTransformations": [
{
"defaultValue": number,
"dimensions": {
"string" : "string"
},
"metricName": "string",
"metricNamespace": "string",
"metricValue": "string",
"unit": "string"
}
]
}
],
"nextToken": "string"
}
Base do nosso State Machine
{
"Comment": "Event-driven workflow",
"StartAt": "ExtractAlarmDetails",
"States": {
"ExtractAlarmDetails": {
"Type": "Pass",
"Output": {
"MetricName": "{% $states.input.detail.configuration.metrics[0].metricStat.metric.name %}",
"MetricNamespace": "{% $states.input.detail.configuration.metrics[0].metricStat.metric.namespace %}"
},
"Next": "DescribeMetricFilter",
"Assign": {
"StartTime": "{% $parse($states.input.detail.state.reasonData).startDate %}",
"EndTime": "{% $parse($states.input.detail.state.reasonData).queryDate %}"
}
},
"DescribeMetricFilter": {
"Type": "Task",
"Resource": "arn:aws:states:::aws-sdk:cloudwatchlogs:describeMetricFilters",
"Arguments": {
"MetricName": "{% $states.input.MetricName %}",
"MetricNamespace": "{% $states.input.MetricNamespace %}"
},
"Output": {
"LogGroupName": "{% $states.result.MetricFilters[0].LogGroupName %}",
"FilterPattern": "{% $states.result.MetricFilters.FilterPattern %}"
},
"Catch": [
{
"ErrorEquals": ["States.ALL"],
"Next": "HandleMetricFilterError",
"Output": {
"error": "{% $states.errorOutput %}"
}
}
],
"Next": "FilterLogEvents"
},
"HandleMetricFilterError": {
"Type": "Fail",
"Cause": "{% $states.errorOutput %}",
"Error": "MetricFilterError"
},
"FilterLogEvents": {
"Type": "Task",
"Resource": "arn:aws:states:::aws-sdk:cloudwatchlogs:filterLogEvents",
"Arguments": {
"LogGroupName": "{% $states.input.LogGroupName %}",
"StartTime": "{% $toMillis($StartTime) %}",
"EndTime": "{% $toMillis($EndTime) %}",
"FilterPattern": "{% $states.input.FilterPattern %}"
},
"Output": {
"events": "{% $states.result.Events %}"
},
"End": true
}
},
"QueryLanguage": "JSONata"
}
Setup Python
- Configurações básicas
mkdir -p strands
cd strands
- Crie o ambiente virtual
python -m venv .venv
- Acesse o ambiente
source .venv/bin/activate
- Instale as dependencias
pip install 'strands-agents[openai]' strands-agents-tools python-dotenv
- Vamos seguir via VsCode
code .
Credenciais
Groq Cloud
- Acesse o site oficial da Groq Cloud
- Faça a criação da sua API KEY e copie
AWS
- No Learner Labs, acesse os detalhes e cliquem em
Show

- Com base nessas credenciais, crie um arquivo
.enve coloque as credenciais da Groq Cloud e da AWS
GROQ_API_KEY=<GROQ_API_KEY>
AWS_ACCESS_KEY_ID=<ACCESS_KEY>
AWS_SECRET_ACCESS_KEY=<SECRET_KEY>
AWS_SESSION_TOKEN=<SESSION_TOKEN>
Integração Strands Agents com Groq Cloud
- Atualmente o Strands Agents não possui uma classe que facilite a comunicação entre a API da Groq
- Por isso, vamos utilizar a da OpenAI, pois é compatível
model = OpenAIModel(
client_args={
"api_key": os.environ.get("GROQ_API_KEY"),
"base_url": "https://api.groq.com/openai/v1",
},
model_id="openai/gpt-oss-20b",
params={"max_tokens": 2048, "temperature": 0.7},
)
Integração com o Discord
- Será pelo Discord que vamos ser notificados a cada Alarme ativo
- Acesse o site do Discord
- Se cadastre ou faça login
- Crie um servidor ou entre em um existente
- Crie ou vá em um canal de texto
- Nas configurações desse canal de texto, acesse o último item, “Integrações”
- Crie o Webhook
- Se quiser colocar uma imagem no seu App Webhook, usarei essa abaixo

Armazenar suas credenciais de forma segura
- Armazene sua chave da Groq de forma segura no AWS Parameter Store
aws ssm put-parameter \
--name "/lambda/analyze-incident/groq-api-key" \
--value "SUA_CHAVE_GROQ_AQUI" \
--type SecureString
- E sua URL do Webhook também, não esqueça
aws ssm put-parameter \
--name "/lambda/discord-webhook/url-webhook" \
--value "https://discord.com/api/webhooks/1234.../abc5..." \
--type SecureString
Criando uma Layer para a Lambda
- Agora vamos agrupar as dependências Python para a Lambda
mkdir -p analyze-incident/python
- Entre na pasta
cd analyze-incident
- Nesse nosso lab, vamos usar somente o package ‘strands-agents[openai]’
pip install \
--platform manylinux2014_aarch64 \
--target python/ \
--python-version 3.10 \
--only-binary=:all: \
'strands-agents[openai]'
- Compacte a layer
zip -r analyze-incident-layer.zip python
Código Python da Lambda Multiagente
- Essa Lambda vai utilizar a Layer que criamos
- Devemos colocar como variável de ambiente o
GROQ_PARAM_NAME
import os
import json
import boto3
from pydantic import BaseModel, Field
from strands import Agent
from strands.models.openai import OpenAIModel
ssm = boto3.client("ssm")
GROQ_PARAM_NAME = os.environ.get("GROQ_PARAM_ARN")
def get_groq_api_key():
"""Busca a chave de API do AWS Parameter Store. Levanta exceção em caso de erro."""
if not GROQ_PARAM_NAME:
raise ValueError("Variável de ambiente GROQ_PARAM_ARN não definida.")
try:
param = ssm.get_parameter(Name=GROQ_PARAM_NAME, WithDecryption=True)
return param["Parameter"]["Value"]
except Exception as e:
raise RuntimeError(
"Não foi possível obter a chave de API do Parameter Store."
) from e
def get_groq_model(model_id: str):
"""Inicializa e retorna o modelo Groq. Levanta exceção em caso de erro."""
api_key = get_groq_api_key()
return OpenAIModel(
client_args={
"api_key": api_key,
"base_url": "https://api.groq.com/openai/v1",
},
model_id=model_id,
params={"max_tokens": 4096, "temperature": 0.5},
)
class IncidentReport(BaseModel):
title: str = Field(..., description="Título do incidente")
incident_type: str = Field(..., description="Tipo de incidente detectado")
description: str = Field(..., description="Resumo do incidente")
diagnosis: str = Field(..., description="Diagnóstico provável")
log_analyzer = Agent(
name="Log Analyzer Agent",
model=get_groq_model("openai/gpt-oss-120b"),
system_prompt=(
"Você é um especialista em análise de logs. "
"Receberá um conjunto de logs como entrada e deve identificar possíveis incidentes, "
"anomalias ou falhas relevantes."
),
)
report_synthesizer = Agent(
name="Report Synthesizer Agent",
model=get_groq_model("openai/gpt-oss-20b"),
system_prompt=(
"Você é um especialista em relatórios de incidentes. "
"Com base na análise recebida, gere um relatório no formato estruturado IncidentReport. "
"Use no máximo 2000 caracteres ao converter para string. "
"Foque mais na parte do diagnóstico do incidente."
),
)
def lambda_handler(event, context):
logs = event.get("events", [])
if not logs:
return {
"statusCode": 400,
"body": json.dumps({"error": "Nenhum log fornecido no evento"})
}
analysis = log_analyzer(json.dumps(logs))
report: IncidentReport = report_synthesizer.structured_output(IncidentReport, str(analysis))
return {
"statusCode": 200,
"body": json.dumps({
"report": report.model_dump()
})
}
Código Python da Lambda do Discord Webhook
- Essa Lambda não vai precisar de Layer
- Devemos colocar como variável de ambiente o
DISCORD_WEBHOOK_PARAM
import os
import json
import boto3
import urllib3
import logging
logger = logging.getLogger()
logger.setLevel(logging.INFO)
http = urllib3.PoolManager()
ssm = boto3.client("ssm")
def get_webhook_url():
try:
param_name = os.environ["DISCORD_WEBHOOK_PARAM"]
response = ssm.get_parameter(Name=param_name, WithDecryption=True)
return response["Parameter"]["Value"]
except Exception as e:
logger.exception("ERRO CRÍTICO: Não foi possível obter a Webhook URL do SSM.")
raise e
WEBHOOK_URL = get_webhook_url()
def parse_sns_message(sns_message_str: str) -> dict:
try:
data = json.loads(sns_message_str)
if "report" in data and isinstance(data["report"], str):
data["report"] = json.loads(data["report"])
return data
except json.JSONDecodeError:
logger.warning("Mensagem SNS não é um JSON válido. Tratando como texto puro.")
return {"raw_message": sns_message_str}
def format_discord_message(data: dict) -> str:
if "report" in data:
report = data["report"]
return (
f"🚨 **Novo Incidente Detectado** 🚨\n\n"
f"**Título:** {report.get('title', 'N/A')}\n"
f"**Tipo:** {report.get('incident_type', 'N/A')}\n"
f"**Descrição:** {report.get('description', 'N/A')}\n"
f"**Diagnóstico:** {report.get('diagnosis', 'N/A')}"
)
raw_message = data.get("raw_message") or json.dumps(data, indent=2)
return f"📢 **Notificação SNS Recebida**:\n```json\n{raw_message}\n```"
def send_to_discord(message: str):
payload = {"content": message}
encoded_body = json.dumps(payload).encode("utf-8")
response = http.request(
"POST",
WEBHOOK_URL,
body=encoded_body,
headers={"Content-Type": "application/json"}
)
if response.status not in (200, 204):
error_message = f"Falha ao enviar para o Discord: {response.status} - {response.data.decode()}"
logger.error(error_message)
raise RuntimeError(error_message)
logger.info(f"Mensagem enviada ao Discord com sucesso. Status: {response.status}")
def lambda_handler(event, context):
logger.info(f"Evento recebido: {json.dumps(event)}")
try:
sns_message_str = event["Records"][0]["Sns"]["Message"]
parsed_data = parse_sns_message(sns_message_str)
discord_message = format_discord_message(parsed_data)
send_to_discord(discord_message)
return {
"statusCode": 200,
"body": json.dumps({"message": "Mensagem enviada ao Discord com sucesso."})
}
except (KeyError, IndexError) as e:
logger.error(f"Erro ao extrair mensagem do evento SNS: {e}")
return {"statusCode": 400, "body": "Formato de evento SNS inválido."}
except Exception:
logger.exception("Ocorreu um erro inesperado durante a execução.")
raise
JSONata do PutObject S3
{
"Body": "{% $parse($states.input.body).report %}",
"Bucket": "reports-<MATRICULA>",
"Key": "{% 'report-' & $string($now()) & '.json' %}"
}
Comandos para testar o Alarme
1. Lambda Function
- Criar Log Stream
aws logs create-log-stream \
--log-group-name /minicurso/cloud-incident \
--log-stream-name "lambda/$(date +%Y-%m-%d)"
- Criar Log Events
aws logs put-log-events \
--log-group-name /minicurso/cloud-incident \
--log-stream-name "lambda/$(date +%Y-%m-%d)" \
--log-events '[
{
"timestamp": '"$(date +%s%3N)"',
"message": "[LAMBDA] START RequestId: a1b2c3d4-5678-90ab-cdef-1234567890ab Version: $LATEST"
},
{
"timestamp": '"$(date +%s%3N)"',
"message": "[LAMBDA] INFO: Processando evento de incidente - severity: HIGH"
},
{
"timestamp": '"$(date +%s%3N)"',
"message": "[LAMBDA] ERROR: Failed to connect to Groq API - Connection timeout after 30s"
},
{
"timestamp": '"$(date +%s%3N)"',
"message": "[LAMBDA] ERROR: Retry attempt 3/3 failed - giving up"
},
{
"timestamp": '"$(date +%s%3N)"',
"message": "[LAMBDA] END RequestId: a1b2c3d4-5678-90ab-cdef-1234567890ab"
}
]'
2. RDS Database
- Criar Log Stream
aws logs create-log-stream \
--log-group-name /minicurso/cloud-incident \
--log-stream-name "rds/$(date +%Y-%m-%d)"
- Criar Log Events
aws logs put-log-events \
--log-group-name /minicurso/cloud-incident \
--log-stream-name "rds/$(date +%Y-%m-%d)" \
--log-events '[
{
"timestamp": '"$(date +%s%3N)"',
"message": "[RDS] '"$(date +%Y-%m-%d)"' ERROR: deadlock detected"
},
{
"timestamp": '"$(date +%s%3N)"',
"message": "[RDS] '"$(date +%Y-%m-%d)"' DETAIL: Process 12346 waits for ShareLock on transaction 98765"
},
{
"timestamp": '"$(date +%s%3N)"',
"message": "[RDS] '"$(date +%Y-%m-%d)"' ERROR: could not serialize access due to concurrent update"
},
{
"timestamp": '"$(date +%s%3N)"',
"message": "[RDS] '"$(date +%Y-%m-%d)"' FATAL: remaining connection slots are reserved for superuser"
}
]'
3. ECS Container
- Criar Log Stream
aws logs create-log-stream \
--log-group-name /minicurso/cloud-incident \
--log-stream-name "ecs/$(date +%Y-%m-%d)"
- Criar Log Events
aws logs put-log-events \
--log-group-name /minicurso/cloud-incident \
--log-stream-name "ecs/$(date +%Y-%m-%d)" \
--log-events '[
{
"timestamp": '"$(date +%s%3N)"',
"message": "[ECS] INFO: Application started on port 8080"
},
{
"timestamp": '"$(date +%s%3N)"',
"message": "[ECS] WARN: High memory usage detected: 85% of 2GB limit"
},
{
"timestamp": '"$(date +%s%3N)"',
"message": "[ECS] ERROR: Connection pool exhausted: max 50 connections reached"
},
{
"timestamp": '"$(date +%s%3N)"',
"message": "[ECS] ERROR: Database query timeout after 5000ms"
},
{
"timestamp": '"$(date +%s%3N)"',
"message": "[ECS] FATAL: Container shutting down due to OOMKilled"
}
]'
4. API Gateway
- Criar Log Stream
aws logs create-log-stream \
--log-group-name /minicurso/cloud-incident \
--log-stream-name "api-gateway/$(date +%Y-%m-%d)"
- Criar Log Events
aws logs put-log-events \
--log-group-name /minicurso/cloud-incident \
--log-stream-name "api-gateway/$(date +%Y-%m-%d)" \
--log-events '[
{
"timestamp": '"$(date +%s%3N)"',
"message": "[API-GW] (req-123) Method request path: /api/incidents"
},
{
"timestamp": '"$(date +%s%3N)"',
"message": "[API-GW] (req-123) ERROR: Execution failed - Malformed Lambda proxy response"
},
{
"timestamp": '"$(date +%s%3N)"',
"message": "[API-GW] (req-123) Method completed with status: 502"
},
{
"timestamp": '"$(date +%s%3N)"',
"message": "[API-GW] (req-124) ERROR: Throttle limit exceeded - Too Many Requests"
}
]'
5. EC2 System
- Criar Log Stream
aws logs create-log-stream \
--log-group-name /minicurso/cloud-incident \
--log-stream-name "ec2/$(date +%Y-%m-%d)"
- Criar Log Events
aws logs put-log-events \
--log-group-name /minicurso/cloud-incident \
--log-stream-name "ec2/$(date +%Y-%m-%d)" \
--log-events '[
{
"timestamp": '"$(date +%s%3N)"',
"message": "[EC2] '"$(date +%Y-%m-%d)"' kernel: ERROR: Out of memory - Kill process 9876 (java) score 850"
},
{
"timestamp": '"$(date +%s%3N)"',
"message": "[EC2] '"$(date +%Y-%m-%d)"' sshd: ERROR: Failed password for invalid user admin from 203.0.113.42"
},
{
"timestamp": '"$(date +%s%3N)"',
"message": "[EC2] '"$(date +%Y-%m-%d)"' nginx: [ERROR] connect() failed (111: Connection refused)"
},
{
"timestamp": '"$(date +%s%3N)"',
"message": "[EC2] '"$(date +%Y-%m-%d)"' disk-monitor: CRITICAL ERROR - Disk usage: 95%"
}
]'
6. Step Functions
- Criar Log Stream
aws logs create-log-stream \
--log-group-name /minicurso/cloud-incident \
--log-stream-name "stepfunctions/$(date +%Y-%m-%d)"
- Criar Log Events
aws logs put-log-events \
--log-group-name /minicurso/cloud-incident \
--log-stream-name "stepfunctions/$(date +%Y-%m-%d)" \
--log-events '[
{
"timestamp": '"$(date +%s%3N)"',
"message": "[STEP-FN] {\"type\":\"ExecutionStarted\",\"input\":\"{\\\"severity\\\":\\\"HIGH\\\"}\"}"
},
{
"timestamp": '"$(date +%s%3N)"',
"message": "[STEP-FN] {\"type\":\"TaskStateEntered\",\"name\":\"FilterEvent\"}"
},
{
"timestamp": '"$(date +%s%3N)"',
"message": "[STEP-FN] {\"type\":\"LambdaFunctionFailed\",\"error\":\"ERROR: TimeoutError - Task timed out after 60 seconds\"}"
},
{
"timestamp": '"$(date +%s%3N)"',
"message": "[STEP-FN] {\"type\":\"ExecutionFailed\",\"cause\":\"ERROR: Lambda function failed\"}"
}
]'
7. Application Load Balancer
- Criar Log Stream
aws logs create-log-stream \
--log-group-name /minicurso/cloud-incident \
--log-stream-name "alb/$(date +%Y-%m-%d)"
- Criar Log Events
aws logs put-log-events \
--log-group-name /minicurso/cloud-incident \
--log-stream-name "alb/$(date +%Y-%m-%d)" \
--log-events '[
{
"timestamp": '"$(date +%s%3N)"',
"message": "[ALB] http '"$(date +%Y-%m-%d)"' app/prod-alb/abc123 203.0.113.42:54321 10.0.1.23:80 0.001 0.002 0.000 200 200 512 1024"
},
{
"timestamp": '"$(date +%s%3N)"',
"message": "[ALB] ERROR http '"$(date +%Y-%m-%d)"' app/prod-alb/abc123 198.51.100.25:12345 10.0.1.23:80 0.003 5.234 0.001 504 504 2048 0"
},
{
"timestamp": '"$(date +%s%3N)"',
"message": "[ALB] ERROR http '"$(date +%Y-%m-%d)"' app/prod-alb/abc123 192.0.2.156:23456 - -1 -1 -1 502 - 1024 0"
}
]'
8. CloudFront
- Criar Log Stream
aws logs create-log-stream \
--log-group-name /minicurso/cloud-incident \
--log-stream-name "cloudfront/$(date +%Y-%m-%d)"
- Criar Log Events
aws logs put-log-events \
--log-group-name /minicurso/cloud-incident \
--log-stream-name "cloudfront/$(date +%Y-%m-%d)" \
--log-events '[
{
"timestamp": '"$(date +%s%3N)"',
"message": "[CLOUDFRONT] '"$(date +%Y-%m-%d)"' IAD89 12345 203.0.113.42 GET example.com /api/data 200"
},
{
"timestamp": '"$(date +%s%3N)"',
"message": "[CLOUDFRONT] ERROR '"$(date +%Y-%m-%d)"' IAD89 12346 198.51.100.25 GET example.com /api/data 504 GatewayTimeout"
},
{
"timestamp": '"$(date +%s%3N)"',
"message": "[CLOUDFRONT] ERROR '"$(date +%Y-%m-%d)"' IAD89 12347 192.0.2.156 GET example.com /api/data 403 AccessDenied"
}
]'
9. DynamoDB
- Criar Log Stream
aws logs create-log-stream \
--log-group-name /minicurso/cloud-incident \
--log-stream-name "dynamodb/$(date +%Y-%m-%d)"
- Criar Log Events
aws logs put-log-events \
--log-group-name /minicurso/cloud-incident \
--log-stream-name "dynamodb/$(date +%Y-%m-%d)" \
--log-events '[
{
"timestamp": '"$(date +%s%3N)"',
"message": "[DYNAMODB] ERROR: ProvisionedThroughputExceededException - Rate of requests exceeds provisioned throughput"
},
{
"timestamp": '"$(date +%s%3N)"',
"message": "[DYNAMODB] ERROR: ConditionalCheckFailedException - The conditional request failed"
},
{
"timestamp": '"$(date +%s%3N)"',
"message": "[DYNAMODB] ERROR: ThrottlingException - Rate exceeded for table users-table"
}
]'
10. S3 Access Logs
- Criar Log Stream
aws logs create-log-stream \
--log-group-name /minicurso/cloud-incident \
--log-stream-name "s3/$(date +%Y-%m-%d)"
- Criar Log Events
aws logs put-log-events \
--log-group-name /minicurso/cloud-incident \
--log-stream-name "s3/$(date +%Y-%m-%d)" \
--log-events '[
{
"timestamp": '"$(date +%s%3N)"',
"message": "[S3] production-bucket ['"$(date +%Y-%m-%d)"'] 203.0.113.42 REST.GET.OBJECT data.json 200"
},
{
"timestamp": '"$(date +%s%3N)"',
"message": "[S3] ERROR production-bucket ['"$(date +%Y-%m-%d)"'] 198.51.100.25 REST.GET.OBJECT secret.json 403 AccessDenied"
},
{
"timestamp": '"$(date +%s%3N)"',
"message": "[S3] ERROR production-bucket ['"$(date +%Y-%m-%d)"'] 192.0.2.100 REST.PUT.OBJECT large-file.zip 500 InternalError"
}
]'