In my previous post, we looked at a Python script designed to automatically clean up idle EBS volumes. However, manually deploying that script, configuring IAM roles, and setting up EventBridge schedules via the AWS Management Console doesn't scale for production.

To achieve true enterprise operational efficiency, everything must be managed as Infrastructure as Code (IaC).

With over 12 years in IT and 6+ years managing scalable AWS infrastructure, I always recommend automating cloud governance architectures from day one. In this article, I will share the complete production-ready Terraform templates to package and deploy our serverless cost optimization engine.

📁 Project Directory Structure

To keep our infrastructure configurations maintainable, we will break our codebase into standard Terraform modules:

aws-cost-optimizer/ ├── main.tf # Core Lambda & EventBridge infrastructure ├── iam.tf # Strict IAM roles and least-privilege policies ├── variables.tf # Customizable deployment variables └── src/ └── lambda_function.py # Our optimization script from post #1 

🛠️ 1. Defining Variables (variables.tf)

First, we establish our variables. This enables different environment setups (like Dev vs. Production) to utilize custom time frames for resource checks.

variable "aws_region" { type = string default = "us-east-1" description = "The target AWS region for deployment" } variable "retention_days" { type = number default = 7 description = "Number of days an EBS volume must be idle before deletion" } 

🔒 2. Strict IAM Security Guardrails (iam.tf)

As an AWS Certified Solutions Architect, I cannot stress enough the importance of the Principle of Least Privilege. Our Lambda execution role should only have permissions to scan and delete what it absolutely needs.

# Create the IAM Role for Lambda Execution resource "aws_iam_role" "lambda_cost_optimizer_role" { name = "lambda-cost-optimizer-execution-role" assume_role_policy = jsonencode({ Version = "2012-10-17" Statement = [{ Action = "sts:AssumeRole" Effect = "Allow" Principal = { Service = "lambda.amazonaws.com" } }] }) } # Attach Strict Least-Privilege Custom Policies resource "aws_iam_role_policy" "lambda_ec2_policy" { name = "lambda-cost-optimizer-ec2-policy" role = aws_iam_role.lambda_cost_optimizer_role.id policy = jsonencode({ Version = "2012-10-17" Statement = [ { Effect = "Allow" Action = [ "ec2:DescribeVolumes", "ec2:CreateSnapshot", "ec2:DeleteVolume" ] Resource = "*" }, { Effect = "Allow" Action = [ "logs:CreateLogGroup", "logs:CreateLogStream", "logs:PutLogEvents" ] Resource = "arn:aws:logs:*:*:*" } ] }) } 

💡 Production Architecture Best Practices

When deploying this inside your organization, implement these two key practices:

  1. Remote State Files: Never store your local terraform.tfstate file on your work machine. Store it safely inside an encrypted Amazon S3 bucket configured with state locking via an Amazon DynamoDB table to prevent concurrent updates.
  2. Environment Isolation: Use Terraform workspaces or distinct folder paths (environments/dev and environments/prod) to thoroughly test code changes in staging environments prior to executing deployments in production data structures.

🚀 Conclusion

By combining AWS serverless computing with Terraform, your organization shifts away from tedious manual monitoring dashboards into an elegant model of fully automated infrastructure lifecycle management.

Do you manage your serverless scripts via cloud consoles, or do you standardize everything through infrastructure as code pipelines? Let's discuss in the comments below!