A Necessidade de Otimizar Custos
Em qualquer ambiente de nuvem, especialmente em contas de desenvolvimento e homologação, os custos podem rapidamente sair do controle. Máquinas virtuais, bancos de dados e serviços ficam ligados 24/7, mesmo que só sejam usados durante o horário de trabalho. Foi observando esse cenário que uma ideia simples surgiu: por que não automatizar o processo de ligar e desligar esses recursos?
Este artigo conta a jornada de como essa ideia, nascida de um projeto pessoal simples, evoluiu para uma solução robusta e foi adotada no meu ambiente de trabalho, utilizando Python, Terraform e GitHub Actions para orquestrar tudo.
  
  
  A Solução em Python: Mão na Massa com boto3
A escolha do Python foi natural. Sua sintaxe limpa e o poder da biblioteca boto3 (o SDK da AWS para Python) tornaram o desenvolvimento rápido e intuitivo. O objetivo era criar duas funções Lambda: uma para "acordar" os recursos no início do dia (resources_start.py) e outra para "colocá-los para dormir" no final (resources_stop.py).
A lógica é direta: para cada serviço da AWS, o script busca os recursos em um determinado estado e aplica a ação desejada.
  
  
  Desligando Recursos (resources_stop.py)
A função de parada itera sobre diferentes serviços, encontra o que está em execução e o desliga.
EC2: Busca por instâncias com o estado running.
# infra/terraform/app/resources_stop.py
# ... (imports e configuração do logger)
def lambda_handler(event, context):
    results = []
    # EC2 - Para todas as instâncias encontradas
    ec2 = boto3.client("ec2")
    instances = ec2.describe_instances(Filters=[{"Name": "instance-state-name", "Values": ["running"]}])
    for reservation in instances["Reservations"]:
        for instance in reservation["Instances"]:
            instance_id = instance["InstanceId"]
            try:
                ec2.stop_instances(InstanceIds=[instance_id])
                # ... (logging e append nos resultados)
            except Exception as e:
                # ... (tratamento de erro)
ECS: Para os serviços ECS, a estratégia é zerar o desiredCount (número de tarefas desejadas), o que efetivamente para as tarefas em execução.
# infra/terraform/app/resources_stop.py
    # ...
    # ECS - Para todos os serviços encontrados
    ecs = boto3.client("ecs")
    clusters = ecs.list_clusters()["clusterArns"]
    for cluster_arn in clusters:
        services = ecs.list_services(cluster=cluster_arn)["serviceArns"]
        for service_arn in services:
            # ...
            try:
                ecs.update_service(cluster=cluster_arn, service=service_arn, desiredCount=0)
                # ... (logging e append nos resultados)
            except Exception as e:
                # ... (tratamento de erro)
RDS e DocumentDB: O processo é semelhante: listar instâncias e clusters e aplicar a ação de stop.
# infra/terraform/app/resources_stop.py
    # ...
    # RDS - Para todas as instâncias encontradas
    rds = boto3.client("rds")
    instances = rds.describe_db_instances()["DBInstances"]
    for instance in instances:
        try:
            rds.stop_db_instance(DBInstanceIdentifier=instance["DBInstanceIdentifier"])
            # ...
        except Exception as e:
            # ...
    # DocumentDB - Para todos os clusters encontrados
    docdb = boto3.client("docdb")
    clusters = docdb.describe_db_clusters()["DBClusters"]
    for cluster in clusters:
        try:
            docdb.stop_db_cluster(DBClusterIdentifier=cluster["DBClusterIdentifier"])
            # ...
        except Exception as e:
            # ...
  
  
  Ligando Recursos (resources_start.py)
A função de início (resources_start.py) segue a mesma lógica, mas com a ação inversa. Para instâncias EC2, busca as que estão stopped e as inicia. Para serviços ECS, define o desiredCount de volta para 1 (ou o valor que for ideal para seu ambiente). Para RDS e DocumentDB, inicia as instâncias e clusters parados.
O uso do boto3 se mostrou extremamente poderoso, permitindo interagir com toda a gama de serviços da AWS de forma programática e consistente.
Infraestrutura como Código: Orquestrando com Terraform
Com os scripts prontos, o próximo passo era provisionar a infraestrutura de forma automatizada. O Terraform foi a escolha óbvia para gerenciar a criação das Lambdas, permissões e agendamentos.
O arquivo main.tf concentra toda a nossa lógica de provisionamento.
- 
Empacotando as Lambdas: Primeiro, usamos o
archive_filepara compactar nossos scripts Python em arquivos.zip, prontos para o deploy.
# infra/terraform/main.tf data "archive_file" "resources_start_lambda" { type = "zip" source_file = "${path.module}/app/resources_start.py" output_path = "${path.module}/app/resources_start.zip" } data "archive_file" "resources_stop_lambda" { # ... (similar para o stop) } - 
Criando as Funções Lambda: Em seguida, definimos as
aws_lambda_function, apontando para os arquivos zipados, definindo o runtime (python3.12) e o handler.
# infra/terraform/main.tf resource "aws_lambda_function" "resources_start" { filename = data.archive_file.resources_start_lambda.output_path function_name = "${var.namespace}-${var.stage}-${var.name}-resources-start" role = aws_iam_role.scheduler_role.arn handler = "resources_start.lambda_handler" source_code_hash = data.archive_file.resources_start_lambda.output_base64sha256 runtime = "python3.12" # ... } resource "aws_lambda_function" "resources_stop" { # ... (similar para o stop) } - 
Gerenciando Permissões com IAM: Um dos pontos cruciais é garantir que as Lambdas tenham permissão para agir sobre outros recursos. Criamos uma
aws_iam_role_policyque concede explicitamente as ações necessárias (ec2:StartInstances,rds:StopDBInstance, etc.).
# infra/terraform/main.tf resource "aws_iam_role_policy" "resource_manager_policy" { name = "${var.namespace}-${var.stage}-${var.name}-resource-manager-policy" role = aws_iam_role.scheduler_role.id policy = jsonencode({ Version = "2012-10-17", Statement = [ { Effect = "Allow", Action = [ "rds:StartDBCluster", "rds:StopDBCluster", # ... outras permissões "ec2:StopInstances" ], Resource = "*" } ] }) } - 
Agendando as Execuções com EventBridge Scheduler: Finalmente, para automatizar a execução, usamos o
aws_scheduler_schedule. Ele nos permite definir uma expressãocron(schedule_expression) para invocar cada Lambda em um horário específico.
# infra/terraform/main.tf resource "aws_scheduler_schedule" "resources_start_schedule" { name = "${var.namespace}-${var.stage}-${var.name}-resources-start-schedule" schedule_expression = var.start_schedule_expression # ex: "cron(0 8 * * ? *)" # ... target { arn = aws_lambda_function.resources_start.arn role_arn = aws_iam_role.eventbridge_scheduler_role[0].arn # ... } } resource "aws_scheduler_schedule" "resources_stop_schedule" { # ... (similar para o stop, com outro schedule_expression) } 
Implantação Contínua com GitHub Actions
Com o código e a infraestrutura definidos, o ciclo se fecha com a automação do deploy. Um workflow simples no GitHub Actions foi criado para aplicar as mudanças do Terraform a cada push na branch principal, garantindo que qualquer alteração nos scripts ou na infra fosse implantada de forma rápida e segura.
O pipeline basicamente consiste em:
- Fazer o checkout do código.
 - Configurar as credenciais da AWS.
 -  Inicializar o Terraform (
terraform init). -  Planejar e aplicar as mudanças (
terraform apply). 
Conclusão: De um "Script Simples" a uma Solução de Valor
O que começou como um exercício para resolver um problema simples se transformou em uma ferramenta valiosa. A automação do gerenciamento de recursos não apenas gerou uma economia de custos mensurável, mas também serviu como uma incrível experiência de aprendizado.
Essa jornada reforçou a importância de:
- Começar simples: Uma solução não precisa ser complexa para ser eficaz.
 - O poder da automação: Tarefas manuais e repetitivas são candidatas perfeitas para automação.
 - Infraestrutura como Código: Ferramentas como o Terraform são essenciais para criar sistemas gerenciáveis, replicáveis e transparentes.
 
Espero que esta experiência inspire outros a olharem para seus próprios desafios diários e a enxergarem neles oportunidades para inovar, aprender e agregar valor, mesmo que a solução comece com um simples script.
              
    
Top comments (2)
Muito bom! A pouco tempo fiz um processo semelhante na empresa em que trabalho. Criei agendamento para ligar/desligar recursos na conta de homologação e ainda fiz um "painel de controle" para equipe de QA poder ligar algum serviço espefício que não precisa estar disponível sempre, consegui reduzir em 70% o valor mensal de AWS.
Muito bom Gabriel, e o mais satisfatório além da redução de custos, e saber que se tornou uma implementação útil!
Best regards! Valeu pelo comentário!