
Ahorre su costo de almacenamiento en AWS con Terraform
Los volúmenes de EBS se recomiendan como almacenamiento para sistemas de archivos y aplicaciones de tipo base de datos. Por lo tanto, se pueden utilizar para almacenar una gran cantidad de datos.
Dado que con EBS, usted paga por la cantidad de GB que aprovisiona por mes hasta que libera el almacenamiento, uno debe asegurarse de que los volúmenes se liberen tan pronto como ya no se utilicen. De hecho, una de las mejores prácticas es crear instantáneas de los volúmenes de EBS no utilizados y eliminarlos para ahorrar costos.
Una solución es usar AWS Config, que tiene una regla y un plan de remediación para EBS no utilizado. Sabiendo que AWS Config tiene un costo, presentaremos en este artículo, una opción de bajo costo utilizando Infraestructura como código a través de Terraform. Este puede ser un laboratorio práctico para quienes preparan el examen de DevOps para profesionales certificados por AWS.
La solución
La solución es bastante simple.

Usamos una función Lambda que:
- Recupera la lista de los volúmenes de EBS disponibles
- Crea una instantánea para cada uno de ellos.
- Elimina los volúmenes de EBS
Una regla de EventBridge programada ejecutará la función según un programa definido.
La estructura del proyecto Terraform es la siguiente:

El archivo main.tf define los recursos de AWS necesarios.
terraform {
required_providers {
aws = {
source = "hashicorp/aws"
version = "~> 3.27"
}
}
required_version = ">= 0.14.9"
}
# default profile
provider "aws" {
profile = "default"
region = lookup(var.props, "region")
}
# loading policies json
data "local_file" "lambda_policy" {
filename = "policy/policy.json"
}
# loading assumeRole json
data "local_file" "lambda_assumeRole_policy" {
filename = "policy/assumeRole.json"
}
# lambda function
resource "aws_lambda_function" "function_ebs" {
filename = "code/index.zip"
function_name = "automatic_ebs_snapshot"
role = aws_iam_role.lambda_assumeRole_policy.arn
handler = "index.lambda_handler"
runtime = "python3.9"
environment {
variables = {
TAG_KEY = lookup(var.parameters,"tag_key"),
TAG_VALUE = lookup(var.parameters,"tag_value")
}
}
timeout = 600
tags = var.tags
}
# iam role resource
resource "aws_iam_role" "lambda_assumeRole_policy" {
name = "ebsSnapshotPolicy"
assume_role_policy = data.local_file.lambda_assumeRole_policy.content
}
# iam policy resource
resource "aws_iam_role_policy" "pol" {
name = "policy"
role = aws_iam_role.lambda_assumeRole_policy.id
policy = replace(data.local_file.lambda_policy.content, "ACCOUNT_ID", lookup(var.props,"account_id")) # substitue the account_id in policy.json for cloudwatch logs
}
# ----- EventBridge rule -----
resource "aws_cloudwatch_event_rule" "event_rule" {
name = "automatic_ebs_snapshot_rule"
description = "Automatic rule for EBS snapshot creation"
schedule_expression = lookup(var.schedule, "start")
is_enabled = true
}
# ----- EventBridge target -----
resource "aws_cloudwatch_event_target" "target" {
target_id = aws_lambda_function.function_ebs.id
rule = aws_cloudwatch_event_rule.event_rule.name
arn = aws_lambda_function.function_ebs.arn
}
# ----- Allow EventBridge to execute Lambda function -----
resource "aws_lambda_permission" "function_ebs_permission" {
statement_id = "function_ebs_permission"
action = "lambda:InvokeFunction"
function_name = "${aws_lambda_function.function_ebs.function_name}"
principal = "events.amazonaws.com"
source_arn = "${aws_cloudwatch_event_rule.event_rule.arn}"
}
Los principales recursos son:
- El rol y la política en la carpeta de políticas que definen los permisos para enumerar volúmenes de EBS, enumerar y actualizar etiquetas y crear instantáneas.
- La función lambda definida en code/index.py asumirá la función de enumerar los volúmenes disponibles de EBS, crear las instantáneas y eliminar los volúmenes.
import boto3
import datetime
import os
import logging
TAG_KEY = os.environ.get('TAG_KEY')
TAG_VALUE = os.environ.get('TAG_VALUE')
logger = logging.getLogger()
logger.setLevel(logging.INFO)
ec2 = boto3.resource('ec2')
# create the tags
def generate_tags(volumeId, tags):
tag_final = []
if tags:
for tag in tags:
if tag['Key']=='Name':
tag['Key'] = 'Volume'
tag_final.append(tag)
tag_final.append({'Key': 'Source', 'Value': 'Automatically generated'})
tag_final.append({'Key': 'Name', 'Value': f'snapshot-ebs-{volumeId}'})
tag_final.append({'Key': TAG_KEY, 'Value': TAG_VALUE})
tag_final.append({'Key': 'DateCreation', 'Value': f'{datetime.datetime.now()}'})
return tag_final
def lambda_handler(event, context):
volumes = ec2.volumes.filter(Filters=[{'Name': 'status', 'Values': ['available']}])
for device in volumes:
snap = ec2.create_snapshot(
VolumeId=device.id,
TagSpecifications=[
{
'ResourceType': 'snapshot',
'Tags': generate_tags(device.id, device.tags)
},
])
logger.info(f'Snapshot {snap.id} created for Volume {device.id}.')
# delete volume
device.delete()
logger.info(f'Volume {device.id} has been deleted.')
logger.info('EBS snapshot creation completed successfully!')
- La regla EventBridge tiene la función lambda como objetivo y se ejecutará según un cronograma definido en la variable schedule.start en variable.tf a continuación:
# account information
variable "props" {
type = map(string)
default = {
region = "us-east-1"
account_id="123456789999"
}
}
# deployment tags; more tags could be added in the mapping
variable "tags" {
type = map(string)
default = {
IaC = "Terraform"
}
}
# environment variables used to tag the snapshot
variable "parameters" {
type = map(string)
default = {
tag_key = "Achive"
tag_value = "automatic"
}
}
# schedule
variable "schedule" {
type = map(string)
default = {
start = "cron(0 10 ? * SUN *)"
}
}
Consulte el repositorio de GitHub para obtener más detalles.
Desplegar
Para poder implementar esta solución, estos son algunos requisitos previos:
- Terraform debe estar instalado
- AWS CLI debe estar instalado
- Se requiere un perfil de IAM con permisos para crear la regla EventBridge y la función Lambda.
El ejecutable build-deploy.sh contiene comandos para validar el código de Terraform e implementar los recursos en la nube de AWS. Una vez que se inicia Terraform, use el siguiente comando:
./build-deploy.sh
Resultado
Seis recursos serán creados por Terraform en AWS como se muestra a continuación:

En la consola de AWS, se crean la función Lambda y su disparador EvenBridge.

Conclusión
Mostramos cómo podemos implementar una solución ligera para reducir el costo de los volúmenes de EBS que no involucra a AWS Config. Este ejercicio es en sí mismo un buen laboratorio práctico para los colegas de DevOps/SysOps, ya que involucra Infraestructura como código con Terraform y automatización en la nube.
Podríamos mejorar aún más esta solución de muchas maneras: automatizar el ciclo de vida de las instantáneas, reemplazar la regla de programación de EventBridge por un patrón de eventos (cambio de estado de EBS), etc.




