Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
17 changes: 16 additions & 1 deletion terraform/account-wide-infrastructure/dev/aws-backup.tf
Original file line number Diff line number Diff line change
@@ -1,5 +1,4 @@

# First, we create an S3 bucket for compliance reports.
resource "aws_s3_bucket" "backup_reports" {
bucket_prefix = "${local.prefix}-backup-reports"
}
Expand Down Expand Up @@ -45,6 +44,22 @@ resource "aws_s3_bucket_policy" "backup_reports_bucket_policy" {
}
}
},
{
Sid = "AllowBackupReportsWrite"
Effect = "Allow"
Principal = {
AWS = "arn:aws:iam::${local.account_id}:role/aws-service-role/reports.backup.amazonaws.com/AWSServiceRoleForBackupReports"
}
Action = "s3:PutObject"
Resource = [
"${aws_s3_bucket.backup_reports.arn}/*",
]
Condition = {
StringEquals = {
"s3:x-amz-acl" = "bucket-owner-full-control"
}
}
}
]
})
}
Expand Down
2 changes: 2 additions & 0 deletions terraform/account-wide-infrastructure/dev/data.tf
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
data "aws_region" "current" {}

data "aws_caller_identity" "current" {}

data "aws_secretsmanager_secret_version" "identities_account_id" {
secret_id = aws_secretsmanager_secret.identities_account_id.name
}
Expand Down
1 change: 1 addition & 0 deletions terraform/account-wide-infrastructure/dev/locals.tf
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ locals {
project = "nhsd-nrlf"
environment = terraform.workspace
prefix = "${local.project}--${local.environment}"
account_id = data.aws_caller_identity.current.account_id

notification_emails = tolist(jsondecode(data.aws_secretsmanager_secret_version.emails.secret_string))
}
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@ resource "aws_backup_plan" "default" {
cold_storage_after = rule.value.lifecycle.cold_storage_after
}
dynamic "copy_action" {
for_each = rule.value.copy_action
for_each = rule.value.copy_action != null ? rule.value.copy_action : []
content {
lifecycle {
delete_after = copy_action.value.delete_after
Expand Down Expand Up @@ -48,7 +48,7 @@ resource "aws_backup_plan" "dynamodb" {
cold_storage_after = rule.value.lifecycle.cold_storage_after
}
dynamic "copy_action" {
for_each = rule.value.copy_action
for_each = rule.value.copy_action != null ? rule.value.copy_action : []
content {
lifecycle {
delete_after = copy_action.value.delete_after
Expand Down
Original file line number Diff line number Diff line change
@@ -1,7 +1,6 @@
resource "aws_sns_topic" "backup" {
name = "${local.resource_name_prefix}-notifications"
kms_master_key_id = var.bootstrap_kms_key_arn
policy = data.aws_iam_policy_document.allow_backup_to_sns.json
}

data "aws_iam_policy_document" "allow_backup_to_sns" {
Expand All @@ -19,12 +18,25 @@ data "aws_iam_policy_document" "allow_backup_to_sns" {
identifiers = ["backup.amazonaws.com"]
}

resources = ["*"]
resources = [
aws_sns_topic.backup.arn
]

sid = "allow_backup"

condition {
test = "StringEquals"
variable = "aws:SourceAccount"
values = ["${data.aws_caller_identity.current.account_id}"]
}
}
}

resource "aws_sns_topic_policy" "backup_sns_policy" {
arn = aws_sns_topic.backup.arn
policy = data.aws_iam_policy_document.allow_backup_to_sns.json
}

resource "aws_sns_topic_subscription" "aws_backup_notifications_email_target" {
count = length(var.notification_target_email_addresses)
topic_arn = aws_sns_topic.backup.arn
Expand Down
187 changes: 187 additions & 0 deletions terraform/account-wide-infrastructure/prod/aws-backup.tf
Original file line number Diff line number Diff line change
@@ -0,0 +1,187 @@

resource "aws_s3_bucket" "backup_reports" {
bucket_prefix = "${local.prefix}-backup-reports"
}

resource "aws_s3_bucket_public_access_block" "backup_reports" {
bucket = aws_s3_bucket.backup_reports.id

block_public_acls = true
block_public_policy = true
ignore_public_acls = true
restrict_public_buckets = true
}

resource "aws_s3_bucket_server_side_encryption_configuration" "backup_reports" {
bucket = aws_s3_bucket.backup_reports.bucket

rule {
apply_server_side_encryption_by_default {
sse_algorithm = "AES256"
}
}
}

resource "aws_s3_bucket_policy" "backup_reports_bucket_policy" {
bucket = aws_s3_bucket.backup_reports.id

policy = jsonencode({
Version = "2012-10-17"
Id = "backup_reports_bucket_policy"
Statement = [
{
Sid = "HTTPSOnly"
Effect = "Deny"
Principal = "*"
Action = "s3:*"
Resource = [
aws_s3_bucket.backup_reports.arn,
"${aws_s3_bucket.backup_reports.arn}/*",
]
Condition = {
Bool = {
"aws:SecureTransport" = "false"
}
}
},
{
Sid = "AllowBackupReportsWrite"
Effect = "Allow"
Principal = {
AWS = "arn:aws:iam::${local.account_id}:role/aws-service-role/reports.backup.amazonaws.com/AWSServiceRoleForBackupReports"
}
Action = "s3:PutObject"
Resource = [
"${aws_s3_bucket.backup_reports.arn}/*",
]
Condition = {
StringEquals = {
"s3:x-amz-acl" = "bucket-owner-full-control"
}
}
}
]
})
}


resource "aws_s3_bucket_ownership_controls" "backup_reports" {
bucket = aws_s3_bucket.backup_reports.id
rule {
object_ownership = "BucketOwnerPreferred"
}
}

resource "aws_s3_bucket_acl" "backup_reports" {
depends_on = [aws_s3_bucket_ownership_controls.backup_reports]

bucket = aws_s3_bucket.backup_reports.id
acl = "private"
}

resource "aws_kms_key" "backup_notifications" {
description = "KMS key for AWS Backup notifications"
deletion_window_in_days = 7
enable_key_rotation = true
policy = jsonencode({
Version = "2012-10-17"
Statement = [
{
Effect = "Allow"
Sid = "Enable IAM User Permissions"
Principal = {
AWS = "arn:aws:iam::${var.assume_account}:root"
}
Action = "kms:*"
Resource = "*"
},
{
Effect = "Allow"
Principal = {
Service = "sns.amazonaws.com"
}
Action = ["kms:GenerateDataKey*", "kms:Decrypt"]
Resource = "*"
},
]
})
}

module "source" {
source = "../modules/backup-source"

backup_copy_vault_account_id = jsondecode(data.aws_secretsmanager_secret_version.backup_destination_parameters.secret_string)["account-id"]
backup_copy_vault_arn = jsondecode(data.aws_secretsmanager_secret_version.backup_destination_parameters.secret_string)["vault-arn"]
environment_name = local.environment
bootstrap_kms_key_arn = aws_kms_key.backup_notifications.arn
project_name = "${local.prefix}-"
reports_bucket = aws_s3_bucket.backup_reports.bucket
terraform_role_arn = "arn:aws:iam::${var.assume_account}:role/${var.assume_role}"

notification_target_email_addresses = local.notification_emails

backup_plan_config = {
"compliance_resource_types" : [
"S3"
],
"enable" : true,
"rules" : [
{
"copy_action" : [{
"delete_after" : 4,
}],
"lifecycle" : {
"delete_after" : 2
},
"name" : "daily_kept_for_2_days",
"schedule" : "cron(0 0 * * ? *)"
}
],
"selection_tag" : "NHSE-Enable-S3-Backup"
}

backup_plan_config_dynamodb = {
"compliance_resource_types" : [
"DynamoDB"
],
"enable" : true,
"rules" : [
{
"name" : "daily",
"schedule" : "cron(0 0 * * ? *)",
"copy_action" : [{
"delete_after" : 4,
}],

"lifecycle" : {
"delete_after" : 2
}
},
{
"name" : "monthly"
"schedule" : "cron(30 0 ? * 4#1)" # first Thursday each month from 00:30
"copy_action" : [{
"cold_storage_after" : 3,
"delete_after" : 100 # ensures there will always be min 3
}],
"lifecycle" : {
"delete_after" : 2
}

},
{
"name" : "weekly" # overlaps with monthly
"schedule" : "cron(30 0 ? * 4)" # every Thursday from 00:30 to precede releases
"copy_action" : [{
"cold_storage_after" : 14 # ensures 2 warm including one from previous release
"delete_after" : 105
}],
"lifecycle" : {
"delete_after" : 2
}

}
],
"selection_tag" : "NHSE-Enable-DDB-Backup"
}
}
6 changes: 6 additions & 0 deletions terraform/account-wide-infrastructure/prod/data.tf
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
data "aws_region" "current" {}

data "aws_caller_identity" "current" {}

data "aws_secretsmanager_secret_version" "identities_account_id" {
secret_id = aws_secretsmanager_secret.identities_account_id.name
}
Expand All @@ -11,3 +13,7 @@ data "aws_secretsmanager_secret" "emails" {
data "aws_secretsmanager_secret_version" "emails" {
secret_id = data.aws_secretsmanager_secret.emails.id
}

data "aws_secretsmanager_secret_version" "backup_destination_parameters" {
secret_id = aws_secretsmanager_secret.backup_destination_parameters.name
}
1 change: 1 addition & 0 deletions terraform/account-wide-infrastructure/prod/locals.tf
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ locals {
project = "nhsd-nrlf"
environment = terraform.workspace
prefix = "${local.project}--${local.environment}"
account_id = data.aws_caller_identity.current.account_id

notification_emails = tolist(jsondecode(data.aws_secretsmanager_secret_version.emails.secret_string))
}
5 changes: 5 additions & 0 deletions terraform/account-wide-infrastructure/prod/secrets.tf
Original file line number Diff line number Diff line change
Expand Up @@ -30,3 +30,8 @@ resource "aws_secretsmanager_secret" "powerbi_gw_recovery_key" {
name = "${local.project}--prod-powerbi-gw-recovery-key"
description = "Recovery key for the PowerBI Gateway EC2 instance"
}

resource "aws_secretsmanager_secret" "backup_destination_parameters" {
name = "${local.prefix}--backup-destination-parameters"
description = "Parameters used to configure the backup destination"
}
Loading