Skip to content

Commit 39b042e

Browse files
ben851Ben Larabie
andauthored
RDS Immutable Backups (#2244)
* creating immutable rds backups * fix --------- Co-authored-by: Ben Larabie <[email protected]>
1 parent ee6478b commit 39b042e

File tree

1 file changed

+232
-0
lines changed

1 file changed

+232
-0
lines changed

aws/rds/backup.tf

Lines changed: 232 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,232 @@
1+
#
2+
# AWS Backup Configuration for RDS
3+
#
4+
# This provides immutable backups with vault lock protection
5+
# to prevent deletion of backups (ransomware protection).
6+
#
7+
8+
# KMS key policy for backup vault
9+
data "aws_iam_policy_document" "backup_vault_kms" {
10+
count = var.env == "dev" ? 1 : 0
11+
12+
# Allow account root full access
13+
statement {
14+
sid = "Enable IAM User Permissions"
15+
effect = "Allow"
16+
17+
principals {
18+
type = "AWS"
19+
identifiers = ["arn:aws:iam::${var.account_id}:root"]
20+
}
21+
22+
actions = ["kms:*"]
23+
resources = ["*"]
24+
}
25+
26+
# Allow AWS Backup service to use the key
27+
statement {
28+
sid = "Allow AWS Backup to use the key"
29+
effect = "Allow"
30+
31+
principals {
32+
type = "Service"
33+
identifiers = ["backup.amazonaws.com"]
34+
}
35+
36+
actions = [
37+
"kms:CreateGrant",
38+
"kms:Decrypt",
39+
"kms:DescribeKey",
40+
"kms:Encrypt",
41+
"kms:GenerateDataKey",
42+
"kms:GenerateDataKeyWithoutPlaintext",
43+
"kms:ReEncrypt*"
44+
]
45+
46+
resources = ["*"]
47+
48+
condition {
49+
test = "StringEquals"
50+
variable = "kms:ViaService"
51+
values = ["backup.${var.region}.amazonaws.com"]
52+
}
53+
}
54+
}
55+
56+
# KMS key for encrypting backups
57+
resource "aws_kms_key" "backup_vault" {
58+
count = var.env == "dev" ? 1 : 0
59+
60+
description = "KMS key for RDS backup vault encryption - ${var.env}"
61+
deletion_window_in_days = 10
62+
enable_key_rotation = true
63+
policy = data.aws_iam_policy_document.backup_vault_kms[0].json
64+
65+
tags = {
66+
CostCenter = "notification-canada-ca-${var.env}"
67+
Terraform = "true"
68+
}
69+
}
70+
71+
resource "aws_kms_alias" "backup_vault" {
72+
count = var.env == "dev" ? 1 : 0
73+
74+
name = "alias/backup-vault-${var.env}"
75+
target_key_id = aws_kms_key.backup_vault[0].key_id
76+
}
77+
78+
# Backup vault with encryption
79+
resource "aws_backup_vault" "rds" {
80+
count = var.env == "dev" ? 1 : 0
81+
82+
name = "notification-canada-ca-${var.env}-rds-vault"
83+
kms_key_arn = aws_kms_key.backup_vault[0].arn
84+
85+
tags = {
86+
CostCenter = "notification-canada-ca-${var.env}"
87+
Terraform = "true"
88+
}
89+
}
90+
91+
# Vault lock configuration for immutability
92+
# This prevents deletion of backups for the specified retention period
93+
resource "aws_backup_vault_lock_configuration" "rds" {
94+
count = var.env == "dev" ? 1 : 0
95+
96+
backup_vault_name = aws_backup_vault.rds[0].name
97+
min_retention_days = 7
98+
max_retention_days = 8
99+
changeable_for_days = 365
100+
}
101+
102+
# IAM role for AWS Backup
103+
resource "aws_iam_role" "backup" {
104+
count = var.env == "dev" ? 1 : 0
105+
106+
name = "AWSBackupRole-${var.env}"
107+
assume_role_policy = data.aws_iam_policy_document.backup_assume_role.json
108+
109+
tags = {
110+
CostCenter = "notification-canada-ca-${var.env}"
111+
Terraform = "true"
112+
}
113+
}
114+
115+
data "aws_iam_policy_document" "backup_assume_role" {
116+
statement {
117+
effect = "Allow"
118+
119+
principals {
120+
type = "Service"
121+
identifiers = ["backup.amazonaws.com"]
122+
}
123+
124+
actions = ["sts:AssumeRole"]
125+
}
126+
}
127+
128+
resource "aws_iam_role_policy_attachment" "backup" {
129+
count = var.env == "dev" ? 1 : 0
130+
131+
policy_arn = "arn:aws:iam::aws:policy/service-role/AWSBackupServiceRolePolicyForBackup"
132+
role = aws_iam_role.backup[0].name
133+
}
134+
135+
resource "aws_iam_role_policy_attachment" "backup_restore" {
136+
count = var.env == "dev" ? 1 : 0
137+
138+
policy_arn = "arn:aws:iam::aws:policy/service-role/AWSBackupServiceRolePolicyForRestores"
139+
role = aws_iam_role.backup[0].name
140+
}
141+
142+
# Backup plan
143+
resource "aws_backup_plan" "rds" {
144+
count = var.env == "dev" ? 1 : 0
145+
146+
name = "notification-canada-ca-${var.env}-rds-plan"
147+
148+
rule {
149+
rule_name = "daily-backup"
150+
target_vault_name = aws_backup_vault.rds[0].name
151+
schedule = "cron(0 7 * * ? *)" # Daily at 7 AM UTC
152+
start_window = 120 # 2 hours
153+
completion_window = 240 # 4 hours
154+
155+
lifecycle {
156+
delete_after = 8 # Match existing backup_retention_period
157+
}
158+
159+
recovery_point_tags = {
160+
CostCenter = "notification-canada-ca-${var.env}"
161+
Terraform = "true"
162+
}
163+
}
164+
165+
tags = {
166+
CostCenter = "notification-canada-ca-${var.env}"
167+
Terraform = "true"
168+
}
169+
}
170+
171+
# Backup selection - target the RDS cluster
172+
resource "aws_backup_selection" "rds" {
173+
count = var.env == "dev" ? 1 : 0
174+
175+
name = "notification-canada-ca-${var.env}-rds-selection"
176+
plan_id = aws_backup_plan.rds[0].id
177+
iam_role_arn = aws_iam_role.backup[0].arn
178+
179+
resources = [
180+
aws_rds_cluster.notification-canada-ca.arn
181+
]
182+
}
183+
184+
# SNS topic for backup notifications
185+
resource "aws_sns_topic" "backup_notifications" {
186+
count = var.env == "dev" ? 1 : 0
187+
188+
name = "notification-canada-ca-${var.env}-backup-notifications"
189+
kms_master_key_id = aws_kms_key.backup_vault[0].id
190+
191+
tags = {
192+
CostCenter = "notification-canada-ca-${var.env}"
193+
Terraform = "true"
194+
}
195+
}
196+
197+
resource "aws_backup_vault_notifications" "rds" {
198+
count = var.env == "dev" ? 1 : 0
199+
200+
backup_vault_name = aws_backup_vault.rds[0].name
201+
sns_topic_arn = aws_sns_topic.backup_notifications[0].arn
202+
backup_vault_events = ["BACKUP_JOB_COMPLETED", "BACKUP_JOB_FAILED", "RESTORE_JOB_COMPLETED", "RESTORE_JOB_FAILED"]
203+
}
204+
205+
# IAM policy for SNS notifications
206+
resource "aws_sns_topic_policy" "backup_notifications" {
207+
count = var.env == "dev" ? 1 : 0
208+
209+
arn = aws_sns_topic.backup_notifications[0].arn
210+
policy = data.aws_iam_policy_document.backup_notifications[0].json
211+
}
212+
213+
data "aws_iam_policy_document" "backup_notifications" {
214+
count = var.env == "dev" ? 1 : 0
215+
216+
statement {
217+
effect = "Allow"
218+
219+
principals {
220+
type = "Service"
221+
identifiers = ["backup.amazonaws.com"]
222+
}
223+
224+
actions = [
225+
"SNS:Publish"
226+
]
227+
228+
resources = [
229+
aws_sns_topic.backup_notifications[0].arn
230+
]
231+
}
232+
}

0 commit comments

Comments
 (0)