Skip to content

Instantly share code, notes, and snippets.

@lucashmsilva
Created May 8, 2021 20:35
Show Gist options
  • Save lucashmsilva/506ca4017547e9da522247608a572bab to your computer and use it in GitHub Desktop.
Save lucashmsilva/506ca4017547e9da522247608a572bab to your computer and use it in GitHub Desktop.
provider "aws" {
region = var.region
# para tentar rodar no localstack
# skip_credentials_validation = true
# skip_metadata_api_check = true
# skip_requesting_account_id = true
# endpoints {
# apigateway = "http://localhost:4566"
# sns = "http://localhost:4566"
# sqs = "http://localhost:4566"
# iam = "http://localhost:4566"
# }
}
variable "region" {
default = "us-west-2"
type = string
}
# ********** SQS e SNS ***********
# Cria tópico SNS que irá fazer o roteamento das mensagens recebidas do API Gateway
resource "aws_sns_topic" "message_broker" {
name = "webhook_types_broker"
}
# Cria as filas SQS e as inscreve no tópico.
resource "aws_sqs_queue" "account_blocked_webhooks" {
name = "account_blocked_webhooks"
delay_seconds = 0
max_message_size = 262144
message_retention_seconds = 1209600
receive_wait_time_seconds = 10
depends_on = [aws_sns_topic.message_broker]
}
resource "aws_sns_topic_subscription" "messages_sqs_target_1" {
topic_arn = aws_sns_topic.message_broker.arn
protocol = "sqs"
endpoint = aws_sqs_queue.account_blocked_webhooks.arn
raw_message_delivery = true
filter_policy = <<EOF
{
"message_type": [
"account_blocked"
]
}
EOF
depends_on = [aws_sqs_queue.account_blocked_webhooks]
}
resource "aws_sqs_queue" "transaction_webhooks" {
name = "transaction_webhooks"
delay_seconds = 0
max_message_size = 262144
message_retention_seconds = 1209600
receive_wait_time_seconds = 10
depends_on = [aws_sns_topic.message_broker]
}
resource "aws_sns_topic_subscription" "messages_sqs_target_2" {
topic_arn = aws_sns_topic.message_broker.arn
protocol = "sqs"
endpoint = aws_sqs_queue.transaction_webhooks.arn
raw_message_delivery = true
filter_policy = <<EOF
{
"message_type": [
"transaction"
]
}
EOF
depends_on = [aws_sqs_queue.transaction_webhooks]
}
resource "aws_sqs_queue" "webhook_sink" {
name = "webhook_sink"
delay_seconds = 0
max_message_size = 262144
message_retention_seconds = 1209600
receive_wait_time_seconds = 10
depends_on = [aws_sns_topic.message_broker]
}
resource "aws_sns_topic_subscription" "messages_sqs_target_3" {
topic_arn = aws_sns_topic.message_broker.arn
protocol = "sqs"
endpoint = aws_sqs_queue.webhook_sink.arn
raw_message_delivery = true
depends_on = [aws_sqs_queue.webhook_sink]
}
# Configura as permissões de SendMessage para o SNS em cada uma das filas
resource "aws_sqs_queue_policy" "account_blocked_webhooks_sqs_policy" {
queue_url = aws_sqs_queue.account_blocked_webhooks.id
policy = <<POLICY
{
"Version": "2012-10-17",
"Id": "sqspolicy",
"Statement": [
{
"Sid": "First",
"Effect": "Allow",
"Principal": {
"Service": "sns.amazonaws.com"
},
"Action": "sqs:SendMessage",
"Resource": "${aws_sqs_queue.account_blocked_webhooks.arn}",
"Condition": {
"ArnEquals": {
"aws:SourceArn": "${aws_sns_topic.message_broker.arn}"
}
}
}
]
}
POLICY
}
resource "aws_sqs_queue_policy" "transaction_webhooks_sqs_policy" {
queue_url = aws_sqs_queue.transaction_webhooks.id
policy = <<POLICY
{
"Version": "2012-10-17",
"Id": "sqspolicy",
"Statement": [
{
"Sid": "First",
"Effect": "Allow",
"Principal": {
"Service": "sns.amazonaws.com"
},
"Action": "sqs:SendMessage",
"Resource": "${aws_sqs_queue.transaction_webhooks.arn}",
"Condition": {
"ArnEquals": {
"aws:SourceArn": "${aws_sns_topic.message_broker.arn}"
}
}
}
]
}
POLICY
}
resource "aws_sqs_queue_policy" "webhook_sink_sqs_policy" {
queue_url = aws_sqs_queue.webhook_sink.id
policy = <<POLICY
{
"Version": "2012-10-17",
"Id": "sqspolicy",
"Statement": [
{
"Sid": "First",
"Effect": "Allow",
"Principal": {
"Service": "sns.amazonaws.com"
},
"Action": "sqs:SendMessage",
"Resource": "${aws_sqs_queue.webhook_sink.arn}",
"Condition": {
"ArnEquals": {
"aws:SourceArn": "${aws_sns_topic.message_broker.arn}"
}
}
}
]
}
POLICY
}
# ********************************
# ********** API Gatewy ***********
# Cria uma nova API REST no API Gateway
resource "aws_api_gateway_rest_api" "api" {
name = "WebhookReceiver"
description = "receive webhooks and pass the payload througth to SNS"
# politica para filtrar IPs que podem chamar a API
# policy = <<EOF
# {
# "Version": "2012-10-17",
# "Statement": [{
# "Effect": "Allow",
# "Principal": "*",
# "Action": "execute-api:Invoke",
# "Resource": "execute-api:/*/*/*"
# },
# {
# "Effect": "Deny",
# "Principal": "*",
# "Action": "execute-api:Invoke",
# "Resource": "execute-api:/*/*/*",
# "Condition": {
# "NotIpAddress": {
# "aws:SourceIp": [""]
# }
# }
# }
# ]
# }
# EOF
endpoint_configuration {
types = ["REGIONAL"]
}
}
# Cria o recurso /message para a API devifinida no API Gateway
resource "aws_api_gateway_resource" "MessageResource" {
rest_api_id = aws_api_gateway_rest_api.api.id
parent_id = aws_api_gateway_rest_api.api.root_resource_id
path_part = "message"
}
# Cria o recurso /message/:type, vinculando ao recurso criado anteriormente
resource "aws_api_gateway_resource" "MessageTypeResource" {
rest_api_id = aws_api_gateway_rest_api.api.id
parent_id = aws_api_gateway_resource.MessageResource.id
path_part = "{type}"
}
# Associa um método POST para o endpoint /message/:type
resource "aws_api_gateway_method" "post_message" {
rest_api_id = aws_api_gateway_rest_api.api.id
resource_id = aws_api_gateway_resource.MessageTypeResource.id
api_key_required = false
http_method = "POST"
authorization = "NONE"
}
# Configura a integração com o SNS (substiução de URL)
resource "aws_api_gateway_integration" "api_sns_integration" {
rest_api_id = aws_api_gateway_rest_api.api.id
resource_id = aws_api_gateway_resource.MessageTypeResource.id
http_method = "POST"
type = "AWS"
integration_http_method = "POST"
passthrough_behavior = "NEVER"
credentials = aws_iam_role.api_sns_role.arn
uri = "arn:aws:apigateway:${var.region}:sns:path/${aws_sns_topic.message_broker.name}"
request_parameters = {
"integration.request.header.Content-Type" = "'application/x-www-form-urlencoded'"
}
# Aqui vem a mágica
request_templates = {
# Action=Publish& Método da API do SNS
# TopicArn=$util.urlEncode('${aws_sns_topic.message_broker.arn}')& Identificação do Tópico
# Message=$util.urlEncode($input.body)& Payload
# MessageAttributes.entry.1.Name=$util.urlEncode('message_type')& Atributo message_type
# MessageAttributes.entry.1.Value.DataType=$util.urlEncode('String')& Tipo de dado do atributo message_type
# MessageAttributes.entry.1.Value.StringValue=$util.urlEncode($input.params('type'))" Valor para o atributo setado com o parametro type do path
"application/json" = "Action=Publish&TopicArn=$util.urlEncode('${aws_sns_topic.message_broker.arn}')&Message=$util.urlEncode($input.body)&MessageAttributes.entry.1.Name=$util.urlEncode('message_type')&MessageAttributes.entry.1.Value.DataType=$util.urlEncode('String')&MessageAttributes.entry.1.Value.StringValue=$util.urlEncode($input.params('type'))"
}
depends_on = [
aws_api_gateway_method.post_message,
aws_api_gateway_resource.MessageTypeResource
]
}
# Trata a resposta de sucesso da integração com o SNS.
resource "aws_api_gateway_method_response" "success" {
rest_api_id = aws_api_gateway_rest_api.api.id
resource_id = aws_api_gateway_resource.MessageTypeResource.id
http_method = aws_api_gateway_method.post_message.http_method
status_code = 200
response_models = {
"application/json" = "Empty"
}
}
# Configura uma resposta de sucesso personalizada para o endpoint.
resource "aws_api_gateway_integration_response" "success" {
rest_api_id = aws_api_gateway_rest_api.api.id
resource_id = aws_api_gateway_resource.MessageTypeResource.id
http_method = aws_api_gateway_method.post_message.http_method
status_code = aws_api_gateway_method_response.success.status_code
selection_pattern = "^2[0-9][0-9]"
response_templates = {
"application/json" = "{\"message\": \"success\"}"
}
depends_on = [aws_api_gateway_integration.api_sns_integration]
}
# Configura permissões
resource "aws_iam_role" "api_sns_role" {
name = "api-sns-role"
assume_role_policy = <<EOF
{
"Version": "2012-10-17",
"Statement": [
{
"Action": "sts:AssumeRole",
"Principal": {
"Service": "apigateway.amazonaws.com"
},
"Effect": "Allow",
"Sid": ""
}
]
}
EOF
}
resource "aws_iam_policy" "api_sns_policy" {
name = "api-sns-perms"
policy = <<EOF
{
"Version": "2012-10-17",
"Statement": [
{
"Effect": "Allow",
"Action": "sns:Publish",
"Resource": "${aws_sns_topic.message_broker.arn}"
}
]
}
EOF
}
resource "aws_iam_role_policy_attachment" "api" {
role = aws_iam_role.api_sns_role.name
policy_arn = aws_iam_policy.api_sns_policy.arn
}
# Faz o deploy da API (acessar o painel da AWS e obter a URL)
resource "aws_api_gateway_deployment" "api" {
rest_api_id = aws_api_gateway_rest_api.api.id
stage_name = "main"
depends_on = [
aws_api_gateway_integration.api_sns_integration,
]
triggers = {
redeployment = sha1(join(",", tolist([
jsonencode(aws_api_gateway_integration.api_sns_integration),
jsonencode(aws_api_gateway_integration_response.success),
jsonencode(aws_api_gateway_method_response.success),
])))
}
}
# *********************************
output "deployment_invoke_url" {
description = "Deployment invoke url"
value = aws_api_gateway_deployment.api.invoke_url
}
#!/bin/bash
# instalar terraform https://learn.hashicorp.com/tutorials/terraform/install-cli
terraform init
terraform apply -auto-approve
export URL_DO_DEPLOYMENT=$(terraform output deployment_invoke_url | tr -d '"')
echo "Fazendo requests de teste:"
curl -H "Content-Type: application/json" -X POST -d \
'{"conta":"bloqueada"}' \
"${URL_DO_DEPLOYMENT}/message/account_blocked"
echo ""
curl -H "Content-Type: application/json" -X POST -d \
'{"transação":"autorizada"}' \
"${URL_DO_DEPLOYMENT}/message/transaction"
echo ""
curl -H "Content-Type: application/json" -X POST -d \
'{"ola":"mundo"}' \
"${URL_DO_DEPLOYMENT}/message/qualquer_outra_coisa"
echo ""
# para limpar tudo
# terraform apply -destroy -auto-approve
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment