Skip to content

Commit e716136

Browse files
committed
NRL-853 WIP cloud backup setup
1 parent 2cbeb7f commit e716136

File tree

31 files changed

+1164
-4
lines changed

31 files changed

+1164
-4
lines changed
Lines changed: 136 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,136 @@
1+
provider "aws" {
2+
alias = "source"
3+
region = "eu-west-2"
4+
}
5+
6+
variable "destination_vault_arn" {
7+
description = "ARN of the backup vault in the destination account"
8+
type = string
9+
default = ""
10+
}
11+
12+
#data "aws_arn" "destination_vault_arn" {
13+
# arn = var.destination_vault_arn
14+
#}
15+
16+
data "aws_secretsmanager_secret" "backup-account-secret" {
17+
name = "nhsd-nrlf--dev--test-backup-account-id"
18+
}
19+
data "aws_secretsmanager_secret_version" "destination_account_id" {
20+
secret_id = data.aws_secretsmanager_secret.backup-account-secret.id
21+
}
22+
23+
locals {
24+
# Adjust these as required
25+
project_name = "dev-backups-poc"
26+
environment_name = "dev"
27+
28+
source_account_id = data.aws_caller_identity.current.account_id
29+
# destination_account_id = data.aws_arn.destination_vault_arn.account
30+
destination_account_id = data.aws_secretsmanager_secret_version.destination_account_id.secret_string
31+
}
32+
33+
# First, we create an S3 bucket for compliance reports. You may already have a module for creating
34+
# S3 buckets with more refined access rules, which you may prefer to use.
35+
36+
resource "aws_s3_bucket" "backup_reports" {
37+
bucket_prefix = "${local.project_name}-backup-reports"
38+
}
39+
40+
# Now we have to configure access to the report bucket.
41+
42+
resource "aws_s3_bucket_ownership_controls" "backup_reports" {
43+
bucket = aws_s3_bucket.backup_reports.id
44+
rule {
45+
object_ownership = "BucketOwnerPreferred"
46+
}
47+
}
48+
49+
resource "aws_s3_bucket_acl" "backup_reports" {
50+
depends_on = [aws_s3_bucket_ownership_controls.backup_reports]
51+
52+
bucket = aws_s3_bucket.backup_reports.id
53+
acl = "private"
54+
}
55+
56+
# We need a key for the SNS topic that will be used for notifications from AWS Backup. This key
57+
# will be used to encrypt the messages sent to the topic before they are sent to the subscribers,
58+
# but isn't needed by the recipients of the messages.
59+
60+
# First we need some contextual data
61+
data "aws_caller_identity" "current" {}
62+
data "aws_region" "current" {}
63+
64+
# Now we can define the key itself
65+
resource "aws_kms_key" "backup_notifications" {
66+
description = "KMS key for AWS Backup notifications"
67+
deletion_window_in_days = 7
68+
enable_key_rotation = true
69+
policy = jsonencode({
70+
Version = "2012-10-17"
71+
Statement = [
72+
{
73+
Effect = "Allow"
74+
Sid = "Enable IAM User Permissions"
75+
Principal = {
76+
AWS = "arn:aws:iam::${local.source_account_id}:root"
77+
}
78+
Action = "kms:*"
79+
Resource = "*"
80+
},
81+
{
82+
Effect = "Allow"
83+
Principal = {
84+
Service = "sns.amazonaws.com"
85+
}
86+
Action = ["kms:GenerateDataKey*", "kms:Decrypt"]
87+
Resource = "*"
88+
},
89+
]
90+
})
91+
}
92+
93+
# Now we can deploy the source and destination modules, referencing the resources we've created above.
94+
95+
module "source" {
96+
source = "../modules/backup-source"
97+
98+
backup_copy_vault_account_id = local.destination_account_id
99+
# backup_copy_vault_arn = data.aws_arn.destination_vault_arn.arn
100+
environment_name = local.environment_name
101+
bootstrap_kms_key_arn = aws_kms_key.backup_notifications.arn
102+
project_name = local.project_name
103+
reports_bucket = aws_s3_bucket.backup_reports.bucket
104+
#terraform_role_arn = data.aws_caller_identity.current.arn
105+
terraform_role_arn = "arn:aws:iam::${var.assume_account}:role/${var.assume_role}"
106+
107+
backup_plan_config = {
108+
"compliance_resource_types" : [
109+
"S3"
110+
],
111+
"rules" : [
112+
{
113+
"copy_action" : {
114+
"delete_after" : 4
115+
},
116+
"lifecycle" : {
117+
"delete_after" : 2
118+
},
119+
"name" : "daily_kept_for_2_days",
120+
"schedule" : "cron(0 0 * * ? *)"
121+
}
122+
],
123+
"selection_tag" : "NHSE-Enable-Backup"
124+
}
125+
# Note here that we need to explicitly disable DynamoDB backups in the source account.
126+
# The default config in the module enables backups for all resource types.
127+
backup_plan_config_dynamodb = {
128+
"compliance_resource_types" : [
129+
"DynamoDB"
130+
],
131+
"rules" : [
132+
],
133+
"enable" : false,
134+
"selection_tag" : "NHSE-Enable-Backup"
135+
}
136+
}

terraform/account-wide-infrastructure/dev/dynamodb__pointers-table.tf

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
module "dev-pointers-table" {
2-
source = "../modules/pointers-table"
3-
name_prefix = "nhsd-nrlf--dev"
2+
source = "../modules/pointers-table"
3+
name_prefix = "nhsd-nrlf--dev"
4+
enable_backups = true
45
}
56

67
module "dev-sandbox-pointers-table" {

terraform/account-wide-infrastructure/dev/s3.tf

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
module "dev-permissions-store-bucket" {
2-
source = "../modules/permissions-store-bucket"
3-
name_prefix = "nhsd-nrlf--dev"
2+
source = "../modules/permissions-store-bucket"
3+
name_prefix = "nhsd-nrlf--dev"
4+
enable_backups = true
45
}
56

67
module "dev-sandbox-permissions-store-bucket" {
@@ -12,6 +13,7 @@ module "dev-truststore-bucket" {
1213
source = "../modules/truststore-bucket"
1314
name_prefix = "nhsd-nrlf--dev"
1415
server_certificate_file = "../../../truststore/server/dev.pem"
16+
enable_backups = true
1517
}
1618

1719
module "dev-sandbox-truststore-bucket" {

terraform/account-wide-infrastructure/modules/backup-source/README.md

Lines changed: 37 additions & 0 deletions
Large diffs are not rendered by default.
Lines changed: 149 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,149 @@
1+
resource "aws_backup_framework" "main" {
2+
# must be underscores instead of dashes
3+
name = replace("${local.resource_name_prefix}-framework", "-", "_")
4+
description = "${var.project_name} Backup Framework"
5+
6+
# Evaluates if recovery points are encrypted.
7+
control {
8+
name = "BACKUP_RECOVERY_POINT_ENCRYPTED"
9+
10+
scope {
11+
tags = {
12+
"environment_name" = var.environment_name
13+
}
14+
}
15+
}
16+
17+
# Evaluates if backup vaults do not allow manual deletion of recovery points with the exception of certain IAM roles.
18+
control {
19+
name = "BACKUP_RECOVERY_POINT_MANUAL_DELETION_DISABLED"
20+
21+
scope {
22+
tags = {
23+
"environment_name" = var.environment_name
24+
}
25+
}
26+
27+
input_parameter {
28+
name = "principalArnList"
29+
value = var.terraform_role_arn
30+
}
31+
}
32+
33+
# Evaluates if recovery point retention period is at least 1 month.
34+
control {
35+
name = "BACKUP_RECOVERY_POINT_MINIMUM_RETENTION_CHECK"
36+
37+
scope {
38+
tags = {
39+
"environment_name" = var.environment_name
40+
}
41+
}
42+
43+
input_parameter {
44+
name = "requiredRetentionDays"
45+
value = "35"
46+
}
47+
}
48+
49+
# Evaluates if backup plan creates backups at least every 1 day and retains them for at least 1 month before deleting them.
50+
control {
51+
name = "BACKUP_PLAN_MIN_FREQUENCY_AND_MIN_RETENTION_CHECK"
52+
53+
scope {
54+
tags = {
55+
"environment_name" = var.environment_name
56+
}
57+
}
58+
59+
input_parameter {
60+
name = "requiredFrequencyUnit"
61+
value = "days"
62+
}
63+
64+
input_parameter {
65+
name = "requiredRetentionDays"
66+
value = "35"
67+
}
68+
69+
input_parameter {
70+
name = "requiredFrequencyValue"
71+
value = "1"
72+
}
73+
}
74+
75+
# Evaluates if resources are protected by a backup plan.
76+
control {
77+
name = "BACKUP_RESOURCES_PROTECTED_BY_BACKUP_PLAN"
78+
79+
scope {
80+
compliance_resource_types = var.backup_plan_config.compliance_resource_types
81+
tags = {
82+
(var.backup_plan_config.selection_tag) = "True"
83+
}
84+
}
85+
}
86+
87+
# Evaluates if resources have at least one recovery point created within the past 1 day.
88+
control {
89+
name = "BACKUP_LAST_RECOVERY_POINT_CREATED"
90+
91+
input_parameter {
92+
name = "recoveryPointAgeUnit"
93+
value = "days"
94+
}
95+
96+
input_parameter {
97+
name = "recoveryPointAgeValue"
98+
value = "1"
99+
}
100+
101+
scope {
102+
compliance_resource_types = var.backup_plan_config.compliance_resource_types
103+
tags = {
104+
(var.backup_plan_config.selection_tag) = "True"
105+
}
106+
}
107+
}
108+
}
109+
110+
resource "aws_backup_framework" "dynamodb" {
111+
count = var.backup_plan_config_dynamodb.enable ? 1 : 0
112+
# must be underscores instead of dashes
113+
name = replace("${local.resource_name_prefix}-dynamodb-framework", "-", "_")
114+
description = "${var.project_name} DynamoDB Backup Framework"
115+
116+
# Evaluates if resources are protected by a backup plan.
117+
control {
118+
name = "BACKUP_RESOURCES_PROTECTED_BY_BACKUP_PLAN"
119+
120+
scope {
121+
compliance_resource_types = var.backup_plan_config_dynamodb.compliance_resource_types
122+
tags = {
123+
(var.backup_plan_config_dynamodb.selection_tag) = "True"
124+
}
125+
}
126+
}
127+
128+
# Evaluates if resources have at least one recovery point created within the past 1 day.
129+
control {
130+
name = "BACKUP_LAST_RECOVERY_POINT_CREATED"
131+
132+
input_parameter {
133+
name = "recoveryPointAgeUnit"
134+
value = "days"
135+
}
136+
137+
input_parameter {
138+
name = "recoveryPointAgeValue"
139+
value = "1"
140+
}
141+
142+
scope {
143+
compliance_resource_types = var.backup_plan_config_dynamodb.compliance_resource_types
144+
tags = {
145+
(var.backup_plan_config_dynamodb.selection_tag) = "True"
146+
}
147+
}
148+
}
149+
}
Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
resource "aws_backup_vault_notifications" "backup_notification" {
2+
count = var.notifications_target_email_address != "" ? 1 : 0
3+
backup_vault_name = aws_backup_vault.main.name
4+
sns_topic_arn = aws_sns_topic.backup[0].arn
5+
backup_vault_events = [
6+
"BACKUP_JOB_COMPLETED",
7+
"RESTORE_JOB_COMPLETED",
8+
"S3_BACKUP_OBJECT_FAILED",
9+
"S3_RESTORE_OBJECT_FAILED",
10+
"COPY_JOB_FAILED"
11+
]
12+
}

0 commit comments

Comments
 (0)