Skip to content

Commit 21c7563

Browse files
author
Alexander Wiechert
committed
first commit
0 parents  commit 21c7563

File tree

13 files changed

+407
-0
lines changed

13 files changed

+407
-0
lines changed

.github/release-drafter.yml

Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,29 @@
1+
2+
name-template: 'v$RESOLVED_VERSION'
3+
tag-template: 'v$RESOLVED_VERSION'
4+
categories:
5+
- title: 'Features'
6+
labels:
7+
- 'feature'
8+
- title: 'Bug Fixes'
9+
labels:
10+
- 'bug'
11+
- title: 'Maintenance'
12+
labels:
13+
- 'chore'
14+
- 'documentation'
15+
change-template: '- $TITLE (#$NUMBER)'
16+
no-changes-template: '- No changes'
17+
18+
autolabeler:
19+
- label: 'feature'
20+
files:
21+
- '*.tf'
22+
- '*.go'
23+
- label: 'documentation'
24+
files:
25+
- '**/*.md'
26+
27+
template: |
28+
## Changes
29+
$CHANGES

.github/workflows/terraform.yml

Lines changed: 59 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,59 @@
1+
name: Terraform CI
2+
3+
on:
4+
pull_request:
5+
branches:
6+
- main
7+
push:
8+
branches:
9+
- main
10+
11+
jobs:
12+
terraform:
13+
name: Terraform Validate, Format, Plan, Security
14+
runs-on: ubuntu-latest
15+
16+
steps:
17+
- name: Checkout code
18+
uses: actions/checkout@v3
19+
20+
- name: Set up Terraform
21+
uses: hashicorp/setup-terraform@v2
22+
23+
# Format Check (root + alle Unterordner)
24+
- name: Terraform Format Check
25+
run: terraform fmt -check -recursive
26+
27+
# Init & Validate (root module)
28+
- name: Terraform Init & Validate (root module)
29+
run: |
30+
terraform init
31+
terraform validate
32+
33+
# Init & Validate (basic example)
34+
- name: Terraform Init & Validate (basic example)
35+
working-directory: examples/basic
36+
run: |
37+
terraform init
38+
terraform validate
39+
40+
# Terraform Plan (basic example)
41+
- name: Terraform Plan (basic example)
42+
working-directory: examples/basic
43+
run: terraform plan -no-color
44+
45+
# Checkov Security Scan (root module)
46+
- name: Run Checkov (root module)
47+
uses: bridgecrewio/checkov-action@v12
48+
with:
49+
directory: .
50+
quiet: true
51+
soft_fail: true
52+
53+
# Checkov Security Scan (basic example)
54+
- name: Run Checkov (basic example)
55+
uses: bridgecrewio/checkov-action@v12
56+
with:
57+
directory: examples/basic
58+
quiet: true
59+
soft_fail: true

.gitignore

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
2+
*.tfstate
3+
*.tfstate.*
4+
.terraform/
5+
.terraform.lock.hcl
6+
crash.log
7+
*.swp
8+
*.DS_Store
9+
.idea/
10+
.vscode/

CHANGELOG.md

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
2+
# Changelog
3+
4+
Alle wichtigen Änderungen an diesem Projekt werden in diesem Dokument dokumentiert.
5+
6+
Das Format basiert auf [Keep a Changelog](https://keepachangelog.com/de/1.0.0/).
7+
8+
## [Unreleased]
9+
10+
- Hinzufügen von .github Actions Workflow
11+
- Erstellen eines Beispielprojekts in `examples/basic`
12+
- README.md und CHANGELOG.md hinzugefügt
13+
14+
## [v1.0.0] – 2024-05-XX
15+
16+
- Erstveröffentlichung
17+
- Überwachung von EBS-Volumes (BurstBalance, ReadOps, WriteOps)
18+
- Dynamische Alarmschwellen pro Volumentyp (gp2, gp3, io1, io2, st1, sc1)
19+
- Tag-basierte Filterung (`Environment = Production`)
20+
- SNS-Integration mit konfigurierbarem Email-Empfänger
21+
- Beispiele und CI/CD-Pipeline mit Checkov-Sicherheitsprüfungen
22+
23+
## Lizenz
24+
25+
Dieses Projekt steht unter der [MIT-Lizenz](./LICENSE).

CONTRIBUTING.md

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
2+
# Contributing
3+
4+
Thank you for considering contributing!
5+
6+
## How to contribute
7+
- Fork the repo
8+
- Create a feature branch
9+
- Run terraform fmt, validate
10+
- Submit a PR
11+
12+
## Code Guidelines
13+
- Use clear commit messages
14+
- Update CHANGELOG.md

LICENSE

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
2+
MIT License
3+
4+
Permission is hereby granted, free of charge, to any person obtaining a copy
5+
of this software and associated documentation files (the "Software"), to deal
6+
in the Software without restriction, including without limitation the rights
7+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
8+
copies of the Software, and to permit persons to whom the Software is
9+
furnished to do so, subject to the following conditions:
10+
11+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND.

README.md

Lines changed: 101 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,101 @@
1+
[![Terraform CI](https://github.com/elastic2ls-com/terraform-aws-ebs-optimization/actions/workflows/terraform.yml/badge.svg)](https://github.com/elastic2ls-com/terraform-aws-ebs-optimization/actions)
2+
![License](https://img.shields.io/badge/license-MIT-brightgreen?logo=mit)
3+
![Status](https://img.shields.io/badge/status-active-brightgreen.svg?logo=git)
4+
[![Sponsor](https://img.shields.io/badge/sponsors-AlexanderWiechert-blue.svg?logo=github-sponsors)](https://github.com/sponsors/AlexanderWiechert/)
5+
[![Contact](https://img.shields.io/badge/website-elastic2ls.com-blue.svg?logo=google-chrome)](https://www.elastic2ls.com/)
6+
[![Terraform Registry](https://img.shields.io/badge/download-blue.svg?logo=terraform&style=social)](https://registry.terraform.io/modules/elastic2ls-com/ebs-optimization/aws/latest)
7+
![OpenTofu Compatible](https://img.shields.io/badge/OpenTofu-Compatible-4E9A06?logo=opentofu)
8+
9+
# terraform-aws-ebs-optimization
10+
11+
Terraform module to monitor and optimize EBS volumes across an AWS account with dynamic CloudWatch alarms for cost-efficient storage.
12+
13+
This module is compatible with both Terraform (>=1.4) and OpenTofu (>=1.4).
14+
15+
---
16+
17+
## Features
18+
19+
- Monitor **BurstBalance**, **ReadOps**, and **WriteOps** per EBS volume.
20+
- Dynamically set alarm thresholds per volume type (gp2, gp3, io1, io2, st1, sc1).
21+
- Automatically exclude irrelevant alarms (e.g., no BurstBalance on st1/sc1).
22+
- Filter volumes by tags (e.g., `Environment = Production`).
23+
- Send alerts to SNS with configurable email subscription.
24+
- Includes example project and CI workflow with security checks.
25+
26+
---
27+
28+
## Usage
29+
30+
```hcl
31+
module "ebs_optimization" {
32+
source = "github.com/elastic2ls-com/terraform-aws-ebs-optimization"
33+
aws_region = "eu-central-1"
34+
tag_filter_key = "Environment"
35+
tag_filter_value = "Production"
36+
sns_topic_name = "ebs-optimization-alerts"
37+
email_endpoint = "finops-team@example.com"
38+
}
39+
```
40+
## Security Best Practices
41+
42+
- Use **least privilege** on SNS subscribers.
43+
- Apply **Terraform state encryption** (S3 + KMS) if storing sensitive resources.
44+
- Periodically review alarm thresholds for changes in workload patterns.
45+
46+
---
47+
48+
## Examples
49+
50+
- [Basic Example](./examples/basic/main.tf)
51+
52+
---
53+
54+
## Variables
55+
56+
| Name | Description | Type | Default |
57+
|---------------------|-------------------------------------|---------|---------------------|
58+
| aws_region | AWS region | string | "eu-central-1" |
59+
| tag_filter_key | Tag key to filter EBS volumes | string | "Environment" |
60+
| tag_filter_value | Tag value to filter EBS volumes | string | "Production" |
61+
| sns_topic_name | Name of the SNS topic for alerts | string | "ebs-alerts-topic" |
62+
| email_endpoint | Email address for SNS subscription | string | n/a (required) |
63+
64+
---
65+
66+
## Outputs
67+
68+
| Name | Description |
69+
|-----------------------|---------------------------------|
70+
| sns_topic_arn | ARN of the created SNS topic |
71+
| filtered_volume_ids | List of monitored EBS volume IDs |
72+
73+
---
74+
75+
## Requirements
76+
77+
- Terraform ≥ 1.4
78+
- AWS Provider ≥ 5.0
79+
80+
---
81+
82+
## CI/CD
83+
84+
This module uses GitHub Actions to run:
85+
86+
- `terraform fmt`
87+
- `terraform validate`
88+
- `terraform plan` on examples
89+
- `checkov` security scan
90+
91+
---
92+
93+
## License
94+
95+
MIT
96+
97+
---
98+
99+
## Maintainers
100+
101+
[elastic2ls](https://github.com/elastic2ls-com)

examples/basic/main.tf

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
module "ebs_optimization" {
2+
source = "../../"
3+
4+
aws_region = "eu-central-1"
5+
tag_filter_key = "Environment"
6+
tag_filter_value = "Production"
7+
sns_topic_name = "ebs-optimization-alerts"
8+
email_endpoint = "finops-team@example.com"
9+
}

main.tf

Lines changed: 94 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,94 @@
1+
# Hole alle EBS-Volumes in der Region
2+
data "aws_ebs_volumes" "all_volumes" {}
3+
4+
# Hole Details zu jedem Volume
5+
data "aws_ebs_volume" "volume_details" {
6+
for_each = toset(data.aws_ebs_volumes.all_volumes.ids)
7+
volume_id = each.key
8+
}
9+
10+
locals {
11+
alarm_thresholds = {
12+
gp2 = { read_ops = 10, write_ops = 10, burst_balance = 20 }
13+
gp3 = { read_ops = 100, write_ops = 100, burst_balance = 20 }
14+
io1 = { read_ops = 500, write_ops = 500, burst_balance = 0 }
15+
io2 = { read_ops = 500, write_ops = 500, burst_balance = 0 }
16+
st1 = { read_ops = 5, write_ops = 5, burst_balance = 0 }
17+
sc1 = { read_ops = 1, write_ops = 1, burst_balance = 0 }
18+
}
19+
20+
filtered_volumes = [
21+
for vol_id, vol in data.aws_ebs_volume.volume_details :
22+
vol_id
23+
if contains(keys(vol.tags), var.tag_filter_key)
24+
&& vol.tags[var.tag_filter_key] == var.tag_filter_value
25+
]
26+
}
27+
28+
resource "aws_sns_topic" "ebs_alerts" {
29+
name = var.sns_topic_name
30+
}
31+
32+
resource "aws_sns_topic_subscription" "email_sub" {
33+
topic_arn = aws_sns_topic.ebs_alerts.arn
34+
protocol = "email"
35+
endpoint = var.email_endpoint
36+
}
37+
38+
# BurstBalance (nur SSD)
39+
resource "aws_cloudwatch_metric_alarm" "burst_balance_low" {
40+
for_each = toset(local.filtered_volumes)
41+
count = contains(["gp2", "gp3", "io1", "io2"], data.aws_ebs_volume.volume_details[each.key].volume_type) ? 1 : 0
42+
43+
alarm_name = "ebs-burst-balance-low-${each.key}"
44+
comparison_operator = "LessThanThreshold"
45+
evaluation_periods = 1
46+
metric_name = "BurstBalance"
47+
namespace = "AWS/EBS"
48+
period = 300
49+
statistic = "Average"
50+
threshold = local.alarm_thresholds[data.aws_ebs_volume.volume_details[each.key].volume_type].burst_balance
51+
alarm_description = "EBS BurstBalance < threshold for volume ${each.key}"
52+
dimensions = {
53+
VolumeId = each.key
54+
}
55+
alarm_actions = [aws_sns_topic.ebs_alerts.arn]
56+
}
57+
58+
# ReadOps (alle Typen)
59+
resource "aws_cloudwatch_metric_alarm" "read_ops_low" {
60+
for_each = toset(local.filtered_volumes)
61+
62+
alarm_name = "ebs-read-ops-low-${each.key}"
63+
comparison_operator = "LessThanThreshold"
64+
evaluation_periods = 1
65+
metric_name = "VolumeReadOps"
66+
namespace = "AWS/EBS"
67+
period = 300
68+
statistic = "Average"
69+
threshold = local.alarm_thresholds[data.aws_ebs_volume.volume_details[each.key].volume_type].read_ops
70+
alarm_description = "EBS ReadOps < threshold for volume ${each.key}"
71+
dimensions = {
72+
VolumeId = each.key
73+
}
74+
alarm_actions = [aws_sns_topic.ebs_alerts.arn]
75+
}
76+
77+
# WriteOps (alle Typen)
78+
resource "aws_cloudwatch_metric_alarm" "write_ops_low" {
79+
for_each = toset(local.filtered_volumes)
80+
81+
alarm_name = "ebs-write-ops-low-${each.key}"
82+
comparison_operator = "LessThanThreshold"
83+
evaluation_periods = 1
84+
metric_name = "VolumeWriteOps"
85+
namespace = "AWS/EBS"
86+
period = 300
87+
statistic = "Average"
88+
threshold = local.alarm_thresholds[data.aws_ebs_volume.volume_details[each.key].volume_type].write_ops
89+
alarm_description = "EBS WriteOps < threshold for volume ${each.key}"
90+
dimensions = {
91+
VolumeId = each.key
92+
}
93+
alarm_actions = [aws_sns_topic.ebs_alerts.arn]
94+
}

output.tf

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
output "sns_topic_arn" {
2+
description = "SNS topic ARN"
3+
value = aws_sns_topic.ebs_alerts.arn
4+
}
5+
6+
output "filtered_volume_ids" {
7+
description = "List of EBS volume IDs being monitored"
8+
value = local.filtered_volumes
9+
}

0 commit comments

Comments
 (0)