diff --git a/.github/dependabot.yml b/.github/dependabot.yml index 4a3f4b594..2f9936497 100644 --- a/.github/dependabot.yml +++ b/.github/dependabot.yml @@ -75,7 +75,7 @@ updates: directories: - "/infrastructure/account" - "/infrastructure/instance" - - "/infrastructure/terraform_aws_backup/**" + - "/infrastructure/backup/**" schedule: interval: "daily" open-pull-requests-limit: 1 diff --git a/infrastructure/backup/destination/.terraform.lock.hcl b/infrastructure/backup/destination/.terraform.lock.hcl new file mode 100644 index 000000000..ca82c5011 --- /dev/null +++ b/infrastructure/backup/destination/.terraform.lock.hcl @@ -0,0 +1,24 @@ +# This file is maintained automatically by "terraform init". +# Manual edits may be lost in future updates. + +provider "registry.terraform.io/hashicorp/aws" { + version = "6.38.0" + hashes = [ + "h1:RDoKIzXmt7H1mNFcNIyRT+nA/gTJyO3+iW9QGN5I2eQ=", + "zh:143f118ae71059a7a7026c6b950da23fef04a06e2362ffa688bef75e43e869ed", + "zh:29ee220a017306effd877e1280f8b2934dc957e16e0e72ca0222e5514d0db522", + "zh:3a31baabf7aea7aa7669f5a3d76f3445e0e6cce5e9aea0279992765c0df12aee", + "zh:4c1908e62040dbc9901d4426ffb253f53e5dae9e3e1a9125311291ee265c8d8c", + "zh:550f4789f5f5b00e16118d4c17770be3ef4535d6b6928af1cf91ebd30f2c263b", + "zh:6537b7b70bf2c127771b0b84e4b726c834d10666b6104f017edae50c67ebae37", + "zh:9b12af85486a96aedd8d7984b0ff811a4b42e3d88dad1a3fb4c0b580d04fa425", + "zh:af2f9cea0c8bdf5b2a2391f2d179a946c117196f7c829b919673cae3b71d2943", + "zh:c53ffa685381aa4e73158fd9f529239f95938dea330e7aca0b32e7b2a1210432", + "zh:d0995e1d64a7ec8bbc79fc3fbec3749f989e07f211a318705c37cd6a7c7d19e4", + "zh:d2348ffcffc1282983d7a5838dd5d61f372152fe6c0d10868cd6473352318750", + "zh:e449312efb73e4747165e689302a68a1df8ba5755e7f59097069acf82c94f011", + "zh:ec3a538d264ef79380e56fdf107ffb6c0446814f07fc5890c36855fe1e03196b", + "zh:f441e69699b22e32c96a8cdd3bbe694ed302c0dcfe867cd9bd683a16df362714", + "zh:f6f8eaa605ff902234d7e9bdab4fda977185fce14f8576f7b622c914c7d98008", + ] +} diff --git a/infrastructure/backup/destination/Makefile b/infrastructure/backup/destination/Makefile new file mode 100644 index 000000000..dcaa99649 --- /dev/null +++ b/infrastructure/backup/destination/Makefile @@ -0,0 +1,43 @@ +-include .env + +environment = prod-backup +workspace_name = backup-destination + +tf_cmd = AWS_PROFILE=$(AWS_PROFILE) terraform +tf_state = -backend-config="bucket=imms-$(environment)-terraform-state-files" + +.PHONY: lock-provider workspace init init-reconfigure plan apply clean destroy import tf-% + +lock-provider: + # Run this only when you install a new terraform provider. This will generate sha code in lock file for all platform + echo "This may take a while. Be patient!" + $(tf_cmd) providers lock -platform=darwin_arm64 -platform=darwin_amd64 -platform=linux_amd64 -platform=windows_amd64 + +workspace: + $(tf_cmd) workspace select -or-create $(workspace_name) && echo "Switched to workspace: $(workspace_name)" + +init: + $(tf_cmd) init $(tf_state) -upgrade $(tf_vars) + +init-reconfigure: + $(tf_cmd) init $(tf_state) -upgrade $(tf_vars) -reconfigure + +plan: workspace + $(tf_cmd) plan $(tf_vars) + +apply: workspace + $(tf_cmd) apply $(tf_vars) + +clean: + rm -rf .terraform + +destroy: workspace + $(tf_cmd) destroy $(tf_vars) + $(tf_cmd) workspace select default + $(tf_cmd) workspace delete $(workspace_name) + +import: + $(tf_cmd) import $(tf_vars) $(to) $(id) + +tf-%: + $(tf_cmd) $* diff --git a/infrastructure/backup/destination/kms_key.tf b/infrastructure/backup/destination/kms_key.tf new file mode 100644 index 000000000..2c2337e89 --- /dev/null +++ b/infrastructure/backup/destination/kms_key.tf @@ -0,0 +1,24 @@ +resource "aws_kms_key" "destination_backup_key" { + description = "KMS key for AWS Backup vaults" + 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::${data.aws_caller_identity.current.account_id}:root" + } + Action = "kms:*" + Resource = "*" + } + ] + }) +} + +resource "aws_kms_alias" "destination_backup_key" { + name = "alias/imms-prod-backup-vault-encryption" + target_key_id = aws_kms_key.destination_backup_key.key_id +} diff --git a/infrastructure/backup/destination/main.tf b/infrastructure/backup/destination/main.tf new file mode 100644 index 000000000..a0ffe949e --- /dev/null +++ b/infrastructure/backup/destination/main.tf @@ -0,0 +1,28 @@ +terraform { + backend "s3" { + region = "eu-west-2" + key = "state" + use_lockfile = true + } + required_version = ">= 1.5.0" +} + +data "aws_caller_identity" "current" {} + +module "aws_backup_destination" { + source = "git::https://github.com/nhsdigital/terraform-aws-backup.git//modules/aws-backup-destination?ref=v1.4.1" + + account_id = data.aws_caller_identity.current.account_id + kms_key = aws_kms_key.destination_backup_key.arn + source_account_id = "664418956997" # Immunisation Prod + source_account_name = "imms-prod" + + # Copied from existing vault - keep or use defaults? + vault_lock_min_retention_days = 30 + vault_lock_max_retention_days = 60 + + # To set once tested + # enable_vault_protection = true + # enable_iam_protection = true + # vault_lock_type = "compliance" +} diff --git a/infrastructure/backup/source/.terraform.lock.hcl b/infrastructure/backup/source/.terraform.lock.hcl new file mode 100644 index 000000000..66f5a3255 --- /dev/null +++ b/infrastructure/backup/source/.terraform.lock.hcl @@ -0,0 +1,68 @@ +# This file is maintained automatically by "terraform init". +# Manual edits may be lost in future updates. + +provider "registry.terraform.io/hashicorp/archive" { + version = "2.7.1" + constraints = "~> 2.0" + hashes = [ + "h1:A7EnRBVm4h9ryO9LwxYnKr4fy7ExPMwD5a1DsY7m1Y0=", + "zh:19881bb356a4a656a865f48aee70c0b8a03c35951b7799b6113883f67f196e8e", + "zh:2fcfbf6318dd514863268b09bbe19bfc958339c636bcbcc3664b45f2b8bf5cc6", + "zh:3323ab9a504ce0a115c28e64d0739369fe85151291a2ce480d51ccbb0c381ac5", + "zh:362674746fb3da3ab9bd4e70c75a3cdd9801a6cf258991102e2c46669cf68e19", + "zh:7140a46d748fdd12212161445c46bbbf30a3f4586c6ac97dd497f0c2565fe949", + "zh:78d5eefdd9e494defcb3c68d282b8f96630502cac21d1ea161f53cfe9bb483b3", + "zh:875e6ce78b10f73b1efc849bfcc7af3a28c83a52f878f503bb22776f71d79521", + "zh:b872c6ed24e38428d817ebfb214da69ea7eefc2c38e5a774db2ccd58e54d3a22", + "zh:cd6a44f731c1633ae5d37662af86e7b01ae4c96eb8b04144255824c3f350392d", + "zh:e0600f5e8da12710b0c52d6df0ba147a5486427c1a2cc78f31eea37a47ee1b07", + "zh:f21b2e2563bbb1e44e73557bcd6cdbc1ceb369d471049c40eb56cb84b6317a60", + "zh:f752829eba1cc04a479cf7ae7271526b402e206d5bcf1fcce9f535de5ff9e4e6", + ] +} + +provider "registry.terraform.io/hashicorp/aws" { + version = "6.38.0" + constraints = "> 5.0.0" + hashes = [ + "h1:RDoKIzXmt7H1mNFcNIyRT+nA/gTJyO3+iW9QGN5I2eQ=", + "zh:143f118ae71059a7a7026c6b950da23fef04a06e2362ffa688bef75e43e869ed", + "zh:29ee220a017306effd877e1280f8b2934dc957e16e0e72ca0222e5514d0db522", + "zh:3a31baabf7aea7aa7669f5a3d76f3445e0e6cce5e9aea0279992765c0df12aee", + "zh:4c1908e62040dbc9901d4426ffb253f53e5dae9e3e1a9125311291ee265c8d8c", + "zh:550f4789f5f5b00e16118d4c17770be3ef4535d6b6928af1cf91ebd30f2c263b", + "zh:6537b7b70bf2c127771b0b84e4b726c834d10666b6104f017edae50c67ebae37", + "zh:9b12af85486a96aedd8d7984b0ff811a4b42e3d88dad1a3fb4c0b580d04fa425", + "zh:af2f9cea0c8bdf5b2a2391f2d179a946c117196f7c829b919673cae3b71d2943", + "zh:c53ffa685381aa4e73158fd9f529239f95938dea330e7aca0b32e7b2a1210432", + "zh:d0995e1d64a7ec8bbc79fc3fbec3749f989e07f211a318705c37cd6a7c7d19e4", + "zh:d2348ffcffc1282983d7a5838dd5d61f372152fe6c0d10868cd6473352318750", + "zh:e449312efb73e4747165e689302a68a1df8ba5755e7f59097069acf82c94f011", + "zh:ec3a538d264ef79380e56fdf107ffb6c0446814f07fc5890c36855fe1e03196b", + "zh:f441e69699b22e32c96a8cdd3bbe694ed302c0dcfe867cd9bd683a16df362714", + "zh:f6f8eaa605ff902234d7e9bdab4fda977185fce14f8576f7b622c914c7d98008", + ] +} + +provider "registry.terraform.io/hashicorp/awscc" { + version = "1.77.0" + constraints = "~> 1.0" + hashes = [ + "h1:9W7PwWFyRvYJcgQi0Gh6YPb5Mbp44DuhkI/ib9JXSiI=", + "zh:0beda7c66f47e23a0fc8d5611e9d1b278fec9fe5e2b824646cc7dcb41490ccda", + "zh:1eac85430d9f2727a67a8b14a875730623aed5593cfe87ef2277077b0f43a516", + "zh:37b35e065e9e5952be05f03f45c5ad3d70083181f6b04b81e23ac2c9f99724c6", + "zh:86a0672e6e2fdbbe6e1481a359b24996dacbc474ab40457fdfd9b544fa004961", + "zh:b4d99b06d87fbc074ea9ad0c2e99fb484b23e418ce3fcb6c8ce6d60d7dbdf463", + "zh:b9bc0e947d8e04f166c736bec9a06ea07841c95fe7d1caea753bdc5131af3a74", + "zh:e29ab5d232f8acb2d07c901744e77366e72e90fd8cffd0a611e97194a65ee94a", + "zh:eb368d3522cfcdfb96b0a64b56b19d2eab9b845dbcf1be2b32d6e8bc98e20015", + "zh:efba81b057ae9aafe79dda5073e90e077976691407a1e7d5127924e15995ae60", + "zh:f25a0591020c5c6390a592a24a9bc0bf94007b6f68b065b0af727d7a54e78a81", + "zh:f5e210d2829285eb1ecaad3c7035c656f191ed45d05c72a9611596739b7825dd", + "zh:f809ab383cca0a5f83072981c64208cbd7fa67e986a86ee02dd2c82333221e32", + "zh:fc9a72cb4264232f603e7fff010068967ee4eee7133fba8b280d7907232781f6", + "zh:fec465402acc4b9df5471dd854e8c6cf9eb36971affa665506caf2f95c8f8e9c", + "zh:ff5ed93c223772444660582e104f349c6b5c28764c4ad8d87fe663ecbd0e9712", + ] +} diff --git a/infrastructure/backup/source/Makefile b/infrastructure/backup/source/Makefile new file mode 100644 index 000000000..71d3f9af2 --- /dev/null +++ b/infrastructure/backup/source/Makefile @@ -0,0 +1,43 @@ +-include .env + +environment = prod +workspace_name = backup-source + +tf_cmd = AWS_PROFILE=$(AWS_PROFILE) terraform +tf_state = -backend-config="bucket=imms-$(environment)-terraform-state-files" + +.PHONY: lock-provider workspace init init-reconfigure plan apply clean destroy import tf-% + +lock-provider: + # Run this only when you install a new terraform provider. This will generate sha code in lock file for all platform + echo "This may take a while. Be patient!" + $(tf_cmd) providers lock -platform=darwin_arm64 -platform=darwin_amd64 -platform=linux_amd64 -platform=windows_amd64 + +workspace: + $(tf_cmd) workspace select -or-create $(workspace_name) && echo "Switched to workspace: $(workspace_name)" + +init: + $(tf_cmd) init $(tf_state) -upgrade $(tf_vars) + +init-reconfigure: + $(tf_cmd) init $(tf_state) -upgrade $(tf_vars) -reconfigure + +plan: workspace + $(tf_cmd) plan $(tf_vars) + +apply: workspace + $(tf_cmd) apply $(tf_vars) + +clean: + rm -rf .terraform + +destroy: workspace + $(tf_cmd) destroy $(tf_vars) + $(tf_cmd) workspace select default + $(tf_cmd) workspace delete $(workspace_name) + +import: + $(tf_cmd) import $(tf_vars) $(to) $(id) + +tf-%: + $(tf_cmd) $* diff --git a/infrastructure/backup/source/kms_key.tf b/infrastructure/backup/source/kms_key.tf new file mode 100644 index 000000000..acf025bc6 --- /dev/null +++ b/infrastructure/backup/source/kms_key.tf @@ -0,0 +1,40 @@ +resource "aws_kms_key" "sns_encrypt_key" { + 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::${data.aws_caller_identity.current.account_id}:root" + } + Action = "kms:*" + Resource = "*" + }, + { + Effect = "Allow" + Principal = { + Service = "sns.amazonaws.com" + } + Action = ["kms:GenerateDataKey*", "kms:Decrypt"] + Resource = "*" + }, + { + Effect = "Allow" + Principal = { + Service = "backup.amazonaws.com" + } + Action = ["kms:GenerateDataKey*", "kms:Decrypt"] + Resource = "*" + }, + ] + }) +} + +resource "aws_kms_alias" "sns_encrypt_key" { + name = "alias/prod/imms-sns-encryption" + target_key_id = aws_kms_key.sns_encrypt_key.key_id +} diff --git a/infrastructure/backup/source/main.tf b/infrastructure/backup/source/main.tf new file mode 100644 index 000000000..441521d0d --- /dev/null +++ b/infrastructure/backup/source/main.tf @@ -0,0 +1,71 @@ +terraform { + # backend "s3" { + # region = "eu-west-2" + # key = "state" + # use_lockfile = true + # } + required_version = ">= 1.5.0" +} + +data "aws_caller_identity" "current" {} + +data "aws_iam_role" "auto_ops" { + name = "auto-ops" +} + +module "aws_backup_source" { + source = "git::https://github.com/nhsdigital/terraform-aws-backup.git//modules/aws-backup-source?ref=v1.4.1" + + environment_name = "prod" + project_name = "ImmsFhirApi" + backup_copy_vault_account_id = "918296738576" + backup_copy_vault_arn = "arn:aws:backup:eu-west-2:918296738576:backup-vault:imms-prod-backup-vault" + reports_bucket = "imms-fhir-api-prod-backup-reports" + bootstrap_kms_key_arn = aws_kms_key.sns_encrypt_key.arn + + # This is the existing email but not sure if anyone is looking at this mailbox + notifications_target_email_address = "ImmsFhirAPiBau_VDS@nhs.net" + + # The existing config has this set to a non-existent role called "terraform" + terraform_role_arns = [data.aws_iam_role.auto_ops.arn] + + # This is the existing backup config. Review retention periods? + backup_plan_config = { + "compliance_resource_types" : [ + "S3" + ], + "rules" : [ + { + "copy_action" : { + "delete_after" : 31 + }, + "lifecycle" : { + "delete_after" : 4 + }, + "name" : "daily_kept_for_4_days", + "schedule" : "cron(00 20 * * ? *)" + } + ], + "selection_tag" : "NHSE-Enable-S3-Backup" + } + + backup_plan_config_dynamodb = { + "compliance_resource_types" : [ + "DynamoDB" + ], + "enable" : true, + "rules" : [ + { + "copy_action" : { + "delete_after" : 31 + }, + "lifecycle" : { + "delete_after" : 4 + }, + "name" : "daily_kept_for_4_days", + "schedule" : "cron(00 20 * * ? *)" + } + ], + "selection_tag" : "NHSE-Enable-Dynamo-Backup" + } +} diff --git a/infrastructure/instance/Makefile b/infrastructure/instance/Makefile index 02c1dabe0..cb5773669 100644 --- a/infrastructure/instance/Makefile +++ b/infrastructure/instance/Makefile @@ -38,9 +38,8 @@ plan-ci: workspace plan-changes: workspace $(tf_cmd) plan $(tf_vars) -out=plan && $(tf_cmd) show -no-color -json plan | jq -r '.resource_changes[] | select(.change.actions[0]=="update" or .change.actions[0]=="create" or .change.actions[0]=="add") | .address' -# TODO - remove --auto-approve once we've switched to the new deployment pipeline in GitHub Actions apply: workspace - $(tf_cmd) apply $(tf_vars) --auto-approve + $(tf_cmd) apply $(tf_vars) apply-ci: workspace $(tf_cmd) apply $(tf_vars) -input=false tfplan @@ -54,7 +53,7 @@ destroy: workspace $(tf_cmd) workspace delete $(sub_environment) graph: workspace - $(tf_cmd) graph + $(tf_cmd) graph output: $(tf_cmd) output -raw $(name) diff --git a/infrastructure/terraform_aws_backup/TODO b/infrastructure/terraform_aws_backup/TODO new file mode 100644 index 000000000..e48bdf203 --- /dev/null +++ b/infrastructure/terraform_aws_backup/TODO @@ -0,0 +1 @@ +Delete this entire directory once the replacement Terraform in the /infrastructure/backup directory has been applied