diff --git a/.devcontainer/README.md b/.devcontainer/README.md index bf6a524f218..a175a0a9dcc 100644 --- a/.devcontainer/README.md +++ b/.devcontainer/README.md @@ -1,14 +1,18 @@ # Dev Container +> [!NOTE] > This is a community supported feature -To assist in the development of `modernisation-platform-environments`, the community have built a [dev container](https://containers.dev/) with the required tooling +To assist with working on this repository, the community has configured a [dev container](https://containers.dev/) with the required tooling. -## Prerequisites +You can run this locally, or with [GitHub Codespaces](https://docs.github.com/en/codespaces/overview). -- GitHub Codespaces +## Locally -or +> [!WARNING] +> This has only been tested on macOS + +### Prerequisites - Docker @@ -16,21 +20,18 @@ or - Dev Containers Extention -## Running - -### GitHub Codespaces - -Launch from GitHub +To launch locally, ensure the prerequisites are met, and then click the button below -### Locally +[![Open in Dev Container](https://raw.githubusercontent.com/ministryofjustice/.devcontainer/refs/heads/main/contrib/badge.svg)](https://vscode.dev/redirect?url=vscode://ms-vscode-remote.remote-containers/cloneInVolume?url=https://github.com/ministryofjustice/modernisation-platform-environments) -1. Ensure prerequisites are met +## GitHub Codespaces -1. Clone repository +> [!IMPORTANT] +> GitHub Codespaces are not currently paid for by the Ministry of Justice and are subject to the quotas [here](https://docs.github.com/en/billing/managing-billing-for-your-products/managing-billing-for-github-codespaces/about-billing-for-github-codespaces#monthly-included-storage-and-core-hours-for-personal-accounts) -1. Open repository in Visual Studio Code +To launch a GitHub Codespace, click the button below -1. Reopen in container +[![Open in Codespace](https://github.com/codespaces/badge.svg)](https://codespaces.new/ministryofjustice/modernisation-platform-environments) ## Tools diff --git a/.github/workflows/code-scanning.yml b/.github/workflows/code-scanning.yml index a80a099382c..fa761adf798 100644 --- a/.github/workflows/code-scanning.yml +++ b/.github/workflows/code-scanning.yml @@ -81,7 +81,7 @@ jobs: fetch-depth: 0 - name: Run Checkov action id: checkov - uses: bridgecrewio/checkov-action@cc23a656ff707900310d6870ca2b4289fa070396 # v12.2917.0 + uses: bridgecrewio/checkov-action@05decb42b761b4c4ce4927c084165bb4705bbcef # v12.2918.0 with: directory: ./ framework: terraform diff --git a/.github/workflows/format-code.yml b/.github/workflows/format-code.yml index b1dea43397b..291666fbab0 100644 --- a/.github/workflows/format-code.yml +++ b/.github/workflows/format-code.yml @@ -40,7 +40,7 @@ jobs: id: ml # You can override MegaLinter flavor used to have faster performances # More info at https://megalinter.io/flavors/ - uses: oxsecurity/megalinter/flavors/terraform@d8c95fc6f2237031fb9e9322b0f97100168afa6e #v8.2.0 + uses: oxsecurity/megalinter/flavors/terraform@1fc052d03c7a43c78fe0fee19c9d648b749e0c01 #v8.3.0 env: # All available variables are described in documentation # https://megalinter.io/configuration/#shared-variables diff --git a/terraform/environments/analytical-platform-compute/kms-keys.tf b/terraform/environments/analytical-platform-compute/kms-keys.tf index a7b2d2bf3da..7ee087235b5 100644 --- a/terraform/environments/analytical-platform-compute/kms-keys.tf +++ b/terraform/environments/analytical-platform-compute/kms-keys.tf @@ -275,6 +275,22 @@ module "mlflow_s3_kms" { tags = local.tags } +module "mojap_compute_athena_s3_kms_eu_west_2" { + #checkov:skip=CKV_TF_1:Module registry does not support commit hashes for versions + #checkov:skip=CKV_TF_2:Module registry does not support tags for versions + + source = "terraform-aws-modules/kms/aws" + version = "3.1.1" + + aliases = ["s3/mojap-compute-athena-query-results-eu-west-2"] + description = "Mojap Athena query bucket S3 KMS key for eu-west-2" + enable_default_policy = true + + deletion_window_in_days = 7 + + tags = local.tags +} + module "mojap_compute_logs_s3_kms_eu_west_2" { #checkov:skip=CKV_TF_1:Module registry does not support commit hashes for versions #checkov:skip=CKV_TF_2:Module registry does not support tags for versions diff --git a/terraform/environments/analytical-platform-compute/s3-buckets.tf b/terraform/environments/analytical-platform-compute/s3-buckets.tf index e0bafa32fc1..03712ea8813 100644 --- a/terraform/environments/analytical-platform-compute/s3-buckets.tf +++ b/terraform/environments/analytical-platform-compute/s3-buckets.tf @@ -136,28 +136,61 @@ module "mojap_compute_logs_bucket_eu_west_1" { ) } -moved { - from = module.mojap_compute_logs_bucket.aws_s3_bucket.this[0] - to = module.mojap_compute_logs_bucket_eu_west_2.aws_s3_bucket.this[0] -} -moved { - from = module.mojap_compute_logs_bucket.aws_s3_bucket_policy.this[0] - to = module.mojap_compute_logs_bucket_eu_west_2.aws_s3_bucket_policy.this[0] -} -moved { - from = module.mojap_compute_logs_bucket.aws_s3_bucket_public_access_block.this[0] - to = module.mojap_compute_logs_bucket_eu_west_2.aws_s3_bucket_public_access_block.this[0] -} -moved { - from = module.mojap_compute_logs_bucket.aws_s3_bucket_server_side_encryption_configuration.this[0] - to = module.mojap_compute_logs_bucket_eu_west_2.aws_s3_bucket_server_side_encryption_configuration.this[0] -} -moved { - from = module.mojap_compute_logs_bucket.aws_s3_bucket_versioning.this[0] - to = module.mojap_compute_logs_bucket_eu_west_2.aws_s3_bucket_versioning.this[0] + +data "aws_iam_policy_document" "athena_query_results_policy_eu_west_2" { + #checkov:skip=CKV_AWS_356:resource "*" limited by condition + statement { + sid = "DenyInsecureTransport" + effect = "Deny" + actions = ["s3:*"] + resources = [ + "arn:aws:s3:::mojap-compute-${local.environment}-athena-query-results-eu-west-2/*", + "arn:aws:s3:::mojap-compute-${local.environment}-athena-query-results-eu-west-2" + ] + principals { + type = "*" + identifiers = ["*"] + } + condition { + test = "Bool" + variable = "aws:SecureTransport" + values = ["false"] + } + } } -moved { - from = aws_iam_policy_document.s3_server_access_logs_policy - to = aws_iam_policy_document.s3_server_access_logs_eu_west_2_policy +module "mojap_compute_athena_query_results_bucket_eu_west_2" { + #checkov:skip=CKV_TF_1:Module registry does not support commit hashes for versions + #checkov:skip=CKV_TF_2:Module registry does not support tags for versions + + source = "terraform-aws-modules/s3-bucket/aws" + version = "4.2.2" + + bucket = "mojap-compute-${local.environment}-athena-query-results-eu-west-2" + + force_destroy = true + + attach_policy = true + policy = data.aws_iam_policy_document.athena_query_results_policy_eu_west_2.json + + object_lock_enabled = false + + versioning = { + status = "Disabled" + } + + server_side_encryption_configuration = { + rule = { + bucket_key_enabled = true + apply_server_side_encryption_by_default = { + kms_master_key_id = module.mojap_compute_athena_s3_kms_eu_west_2.key_arn + sse_algorithm = "aws:kms" + } + } + } + + tags = merge( + local.tags, + { "backup" = "false" } + ) } diff --git a/terraform/environments/analytical-platform-ingestion/environment-configuration.tf b/terraform/environments/analytical-platform-ingestion/environment-configuration.tf index 71ad724ce2a..252fc58ef8c 100644 --- a/terraform/environments/analytical-platform-ingestion/environment-configuration.tf +++ b/terraform/environments/analytical-platform-ingestion/environment-configuration.tf @@ -92,20 +92,6 @@ locals { egress_bucket = module.bold_egress_bucket.s3_bucket_id egress_bucket_kms_key = module.s3_bold_egress_kms.key_arn } - "darren-brooke" = { - ssh_key = "ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABAQDAxeaj85/JshqYMQ1B97TtHyy81oF3L33s89NWCIiHSM/Hql6aFfxCCivsN4Y1OZic8S5drgxe7MdETaWeEKfaWIMgqESGOw5yhCuNSEvt896cc0hSU8/ZwUZrTzYfiCAwqBQHI13JBAP7VcWBR6v6CYQL8JB7lSEvq7vY2BJJ4N9HchlXBHvxHHOu7Y6+ta7BrODvCc0zLHWANE65U4DmZpXmwHHsBao4cOUIlrBIDIAGtXAJB/L+cByH2OPMsRPhUe2UMfTgRHCJdekics/7DzrR+hhZRnHM9du52TFT89eAKpQGpp0wEkFoYKntXesGFr1R/uhRtqzanzBggXIv db@ubuntu" - cidr_blocks = ["54.37.241.156/30"] - egress_bucket = module.ext_2024_egress_bucket.s3_bucket_id - egress_bucket_kms_key = module.s3_ext_2024_egress_kms.key_arn - - } - "aaron-willetts" = { - ssh_key = "ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIAtHz+QozotArRIjRcmD4GDdiQLtXPTX+GGAXqpeqpBZ aaron@kali" - cidr_blocks = ["167.71.136.237/32"] - egress_bucket = module.ext_2024_egress_bucket.s3_bucket_id - egress_bucket_kms_key = module.s3_ext_2024_egress_kms.key_arn - - } } /* DataSync */ diff --git a/terraform/environments/analytical-platform-ingestion/ext-user-2024.tf b/terraform/environments/analytical-platform-ingestion/ext-user-2024.tf deleted file mode 100644 index dd723bd1057..00000000000 --- a/terraform/environments/analytical-platform-ingestion/ext-user-2024.tf +++ /dev/null @@ -1,102 +0,0 @@ -#tfsec:ignore:avd-aws-0088 - The bucket policy is attached to the bucket -#tfsec:ignore:avd-aws-0132 - The bucket policy is attached to the bucket -module "ext_2024_egress_bucket" { - #checkov:skip=CKV_TF_1:Module registry does not support commit hashes for versions - - source = "terraform-aws-modules/s3-bucket/aws" - version = "4.1.2" - - bucket = "mojap-ingestion-${local.environment}-ext-2024-egress" - - force_destroy = true - - versioning = { - enabled = true - } - - server_side_encryption_configuration = { - rule = { - apply_server_side_encryption_by_default = { - kms_master_key_id = module.s3_ext_2024_egress_kms.key_arn - sse_algorithm = "aws:kms" - } - } - } -} - -module "s3_ext_2024_egress_kms" { - #checkov:skip=CKV_TF_1:Module registry does not support commit hashes for versions - - source = "terraform-aws-modules/kms/aws" - version = "3.1.0" - - aliases = ["s3/ext-2024-egress"] - description = "Used in the External 2024 Egress Solution" - enable_default_policy = true - key_statements = [ - { - sid = "AllowReadOnlyRole" - actions = [ - "kms:Encrypt", - "kms:GenerateDataKey" - ] - resources = ["*"] - effect = "Allow" - principals = [ - { - type = "AWS" - identifiers = ["arn:aws:iam::${local.environment_management.account_ids[terraform.workspace]}:role/security-read-only"] - } - ] - } - ] - deletion_window_in_days = 7 -} - -data "aws_iam_policy_document" "ext_2024_target_bucket_policy" { - statement { - sid = "LandingPermissions" - effect = "Allow" - principals { - type = "AWS" - identifiers = ["arn:aws:iam::471112983409:role/transfer"] - } - actions = [ - "s3:GetObject", - "s3:PutObject", - "s3:DeleteObject", - "s3:PutObjectTagging" - ] - resources = [ - "arn:aws:s3:::mojap-ingestion-${local.environment}-ext-2024-target/*", - "arn:aws:s3:::mojap-ingestion-${local.environment}-ext-2024-target" - ] - } -} - -#tfsec:ignore:avd-aws-0088 - The bucket policy is attached to the bucket -#tfsec:ignore:avd-aws-0132 - The bucket policy is attached to the bucket -module "ext_2024_target_bucket" { - #checkov:skip=CKV_TF_1:Module registry does not support commit hashes for versions - - source = "terraform-aws-modules/s3-bucket/aws" - version = "4.1.2" - - bucket = "mojap-ingestion-${local.environment}-ext-2024-target" - - force_destroy = true - - versioning = { - enabled = true - } - attach_policy = true - policy = data.aws_iam_policy_document.ext_2024_target_bucket_policy.json - - server_side_encryption_configuration = { - rule = { - apply_server_side_encryption_by_default = { - sse_algorithm = "AES256" - } - } - } -} diff --git a/terraform/environments/corporate-information-system/iam.tf b/terraform/environments/corporate-information-system/iam.tf index aa2fdb4a3b8..4a6865831b6 100644 --- a/terraform/environments/corporate-information-system/iam.tf +++ b/terraform/environments/corporate-information-system/iam.tf @@ -74,44 +74,44 @@ resource "aws_iam_role_policy" "cis_s3fs_policy" { Version = "2012-10-17" Statement = [ { - "Action": [ - "s3:*" + "Action" : [ + "s3:*" ], - "Resource": [ - "arn:aws:s3:::laa-software-bucket2", - "arn:aws:s3:::laa-software-bucket2/*", - "arn:aws:s3:::laa-software-library", - "arn:aws:s3:::laa-software-library/*", - "arn:aws:s3:::laa-cis-inbound-production", - "arn:aws:s3:::laa-cis-inbound-production/*", - "arn:aws:s3:::laa-cis-outbound-production", - "arn:aws:s3:::laa-cis-outbound-production/*", - "arn:aws:s3:::laa-ccms-outbound-production", - "arn:aws:s3:::laa-ccms-outbound-production/*", - "arn:aws:s3:::laa-ccms-inbound-production", - "arn:aws:s3:::laa-ccms-inbound-production/*" + "Resource" : [ + "arn:aws:s3:::laa-software-bucket2", + "arn:aws:s3:::laa-software-bucket2/*", + "arn:aws:s3:::laa-software-library", + "arn:aws:s3:::laa-software-library/*", + "arn:aws:s3:::laa-cis-inbound-production", + "arn:aws:s3:::laa-cis-inbound-production/*", + "arn:aws:s3:::laa-cis-outbound-production", + "arn:aws:s3:::laa-cis-outbound-production/*", + "arn:aws:s3:::laa-ccms-outbound-production", + "arn:aws:s3:::laa-ccms-outbound-production/*", + "arn:aws:s3:::laa-ccms-inbound-production", + "arn:aws:s3:::laa-ccms-inbound-production/*" ], - "Effect": "Allow" - }, - { - "Action": [ - "logs:CreateLogGroup", - "logs:CreateLogStream", - "logs:DescribeLogStreams", - "logs:PutRetentionPolicy", - "logs:PutLogEvents", - "ec2:DescribeInstances" - ], - "Resource": "*", - "Effect": "Allow" - }, - { - "Action": [ - "ec2:CreateTags" - ], - "Resource": "*", - "Effect": "Allow" - } + "Effect" : "Allow" + }, + { + "Action" : [ + "logs:CreateLogGroup", + "logs:CreateLogStream", + "logs:DescribeLogStreams", + "logs:PutRetentionPolicy", + "logs:PutLogEvents", + "ec2:DescribeInstances" + ], + "Resource" : "*", + "Effect" : "Allow" + }, + { + "Action" : [ + "ec2:CreateTags" + ], + "Resource" : "*", + "Effect" : "Allow" + } ] }) } \ No newline at end of file diff --git a/terraform/environments/corporate-staff-rostering/locals.tf b/terraform/environments/corporate-staff-rostering/locals.tf index 27bb2195852..e44f27dc5e9 100644 --- a/terraform/environments/corporate-staff-rostering/locals.tf +++ b/terraform/environments/corporate-staff-rostering/locals.tf @@ -26,6 +26,7 @@ locals { "ec2_linux", "ec2_instance_linux", "ec2_instance_oracle_db_with_backup", + "ssm_command", ] cloudwatch_metric_alarms_default_actions = ["pagerduty"] cloudwatch_metric_oam_links_ssm_parameters = ["hmpps-oem-${local.environment}"] @@ -45,6 +46,7 @@ locals { enable_s3_db_backup_bucket = true enable_s3_shared_bucket = true enable_s3_software_bucket = true + enable_ssm_command_monitoring = true s3_iam_policies = ["EC2S3BucketWriteAndDeleteAccessPolicy"] software_bucket_name = "csr-software" } diff --git a/terraform/environments/corporate-staff-rostering/main.tf b/terraform/environments/corporate-staff-rostering/main.tf index a23f7e6d41b..d234a1e3a07 100644 --- a/terraform/environments/corporate-staff-rostering/main.tf +++ b/terraform/environments/corporate-staff-rostering/main.tf @@ -74,6 +74,7 @@ module "baseline" { ) cloudwatch_metric_alarms = merge( + module.baseline_presets.cloudwatch_metric_alarms_baseline, lookup(local.baseline_all_environments, "cloudwatch_metric_alarms", {}), lookup(local.baseline_environment_specific, "cloudwatch_metric_alarms", {}), ) @@ -177,6 +178,11 @@ module "baseline" { lookup(local.baseline_environment_specific, "s3_buckets", {}), ) + schedule_alarms_lambda = merge( + lookup(local.baseline_all_environments, "schedule_alarms_lambda", {}), + lookup(local.baseline_environment_specific, "schedule_alarms_lambda", {}), + ) + secretsmanager_secrets = merge( module.baseline_presets.secretsmanager_secrets, lookup(local.baseline_all_environments, "secretsmanager_secrets", {}), diff --git a/terraform/environments/delius-core/files/user_outbound_table_mapping.json b/terraform/environments/delius-core/files/user_outbound_table_mapping.json index f8a2e966334..4d8b7128d6e 100644 --- a/terraform/environments/delius-core/files/user_outbound_table_mapping.json +++ b/terraform/environments/delius-core/files/user_outbound_table_mapping.json @@ -21,6 +21,18 @@ }, "rule-action": "include", "filters": [] - } + }, + { + "rule-type": "transformation", + "rule-id": "32", + "rule-name": "remove_staff_id", + "rule-target": "column", + "object-locator": { + "schema-name": "DELIUS_APP_SCHEMA", + "table-name": "USER_", + "column-name": "STAFF_ID" + }, + "rule-action": "remove-column" + } ] } diff --git a/terraform/environments/delius-core/locals_development.tf b/terraform/environments/delius-core/locals_development.tf index 5eda99ec858..9cc30fcb7c0 100644 --- a/terraform/environments/delius-core/locals_development.tf +++ b/terraform/environments/delius-core/locals_development.tf @@ -76,10 +76,11 @@ locals { delius_microservices_configs_dev = { weblogic = { - image_tag = "6.2.0.3" - container_port = 8080 - container_memory = 4096 - container_cpu = 2048 + image_tag = "6.2.0.3" + container_port = 8080 + container_memory = 4096 + container_cpu = 2048 + task_definition_revision = 9 } weblogic_eis = { @@ -129,6 +130,6 @@ locals { user_target_endpoint = { write_database = "DMDNDA" } - is-production = local.is-production + is-production = false } } diff --git a/terraform/environments/delius-core/locals_preproduction.tf b/terraform/environments/delius-core/locals_preproduction.tf index 813631aed0f..2b3601c34eb 100644 --- a/terraform/environments/delius-core/locals_preproduction.tf +++ b/terraform/environments/delius-core/locals_preproduction.tf @@ -140,7 +140,8 @@ locals { user_target_endpoint = { write_database = "PRENDA" } - is-production = local.is-production + # Auditing from the Pre-Prod environment is considered production data + is-production = true } } diff --git a/terraform/environments/delius-core/locals_stage.tf b/terraform/environments/delius-core/locals_stage.tf index c68bf61590d..f082743a0ef 100644 --- a/terraform/environments/delius-core/locals_stage.tf +++ b/terraform/environments/delius-core/locals_stage.tf @@ -140,6 +140,7 @@ locals { user_target_endpoint = { write_database = "STGNDA" } - is-production = local.is-production + # Auditing from the Stage environment is considered production data + is-production = true } } diff --git a/terraform/environments/delius-core/locals_test.tf b/terraform/environments/delius-core/locals_test.tf index d99ea8448df..6d4fcfa6497 100644 --- a/terraform/environments/delius-core/locals_test.tf +++ b/terraform/environments/delius-core/locals_test.tf @@ -128,6 +128,6 @@ locals { read_database = "TSTNDA" } user_target_endpoint = {} - is-production = local.is-production + is-production = false } } diff --git a/terraform/environments/delius-core/modules/components/dms/cloudwatch-alarms.tf b/terraform/environments/delius-core/modules/components/dms/cloudwatch-alarms.tf index 45fc07e546b..ac7017bfbc7 100644 --- a/terraform/environments/delius-core/modules/components/dms/cloudwatch-alarms.tf +++ b/terraform/environments/delius-core/modules/components/dms/cloudwatch-alarms.tf @@ -1,7 +1,34 @@ # SNS topic for monitoring to send alarms to -resource "aws_sns_topic" "dms_alerting" { - name = "delius-dms-alerting" +resource "aws_sns_topic" "dms_alerts_topic" { + name = "delius-dms-alerts-topic" kms_master_key_id = var.account_config.kms_keys.general_shared + + http_success_feedback_role_arn = aws_iam_role.sns_logging_role.arn + http_success_feedback_sample_rate = 100 + http_failure_feedback_role_arn = aws_iam_role.sns_logging_role.arn +} + +resource "aws_iam_role" "sns_logging_role" { + name = "sns-logging-role" + + assume_role_policy = jsonencode({ + "Version": "2012-10-17", + "Statement": [ + { + "Action": "sts:AssumeRole", + "Principal": { + "Service": "sns.amazonaws.com" + }, + "Effect": "Allow", + "Sid": "" + } + ] + }) +} + +resource "aws_iam_role_policy_attachment" "attach_sns_policy" { + role = aws_iam_role.sns_logging_role.name + policy_arn = "arn:aws:iam::aws:policy/service-role/AmazonSNSRole" } # Create a map of all possible replication tasks, so those that exist may have alarms applied to them. @@ -74,8 +101,8 @@ resource "aws_cloudwatch_metric_alarm" "dms_cdc_latency_source" { evaluation_periods = 3 period = 120 actions_enabled = true - alarm_actions = [aws_sns_topic.dms_alerting.arn] - ok_actions = [aws_sns_topic.dms_alerting.arn] + alarm_actions = [aws_sns_topic.dms_alerts_topic.arn] + ok_actions = [aws_sns_topic.dms_alerts_topic.arn] dimensions = { ReplicationInstanceIdentifier = aws_dms_replication_instance.dms_replication_instance.replication_instance_id # We only need to final element of the replication task ID (after the last :) @@ -96,8 +123,8 @@ resource "aws_cloudwatch_metric_alarm" "dms_cdc_latency_target" { evaluation_periods = 3 period = 120 actions_enabled = true - alarm_actions = [aws_sns_topic.dms_alerting.arn] - ok_actions = [aws_sns_topic.dms_alerting.arn] + alarm_actions = [aws_sns_topic.dms_alerts_topic.arn] + ok_actions = [aws_sns_topic.dms_alerts_topic.arn] dimensions = { ReplicationInstanceIdentifier = aws_dms_replication_instance.dms_replication_instance.replication_instance_id # We only need to final element of the replication task ID (after the last :) @@ -131,9 +158,153 @@ locals { module "pagerduty_core_alerts" { #checkov:skip=CKV_TF_1 depends_on = [ - aws_sns_topic.dms_alerting + aws_sns_topic.dms_alerts_topic ] source = "github.com/ministryofjustice/modernisation-platform-terraform-pagerduty-integration?ref=v2.0.0" - sns_topics = [aws_sns_topic.dms_alerting.name] + sns_topics = [aws_sns_topic.dms_alerts_topic.name] pagerduty_integration_key = local.pagerduty_integration_keys[local.integration_key_lookup] } + + +# Raising a Cloudwatch Alarm on a DMS Replication Task Event is not directly possible using the +# Cloudwatch Alarm Integration in PagerDuty as the JSON payload is different. Therefore, as +# workaround for this we create a custom Cloudwatch Metric which is populated by the replication event and +# create a Cloudwatch Alarm on this Metric in the usual way to allow for raising alarms. + +# Create Role which allows Lamdba to put a custom cloudwatch metric +resource "aws_iam_role" "lambda_put_metric_data_role" { + name = "lambda-put-metric-data-role" + + assume_role_policy = jsonencode({ + Version = "2012-10-17", + Statement = [ + { + Action = "sts:AssumeRole", + Effect = "Allow", + Principal = { + Service = "lambda.amazonaws.com" + } + } + ] + }) +} + +resource "aws_iam_policy" "lambda_put_metric_data_policy" { + name = "lambda-put-metric-data-policy" + + policy = jsonencode({ + Version = "2012-10-17", + Statement = [ + { + Effect = "Allow", + Action = [ + "cloudwatch:PutMetricData" + ], + Resource = "*" + } + ] + }) +} + +resource "aws_iam_role_policy_attachment" "lambda_put_metric_data_policy_attach" { + role = aws_iam_role.lambda_put_metric_data_role.name + policy_arn = aws_iam_policy.lambda_put_metric_data_policy.arn +} + +# Allow Cloudwatch Logging +resource "aws_iam_role_policy_attachment" "lambda_put_metric_data_logging_attach" { + role = aws_iam_role.lambda_put_metric_data_role.name + policy_arn = "arn:aws:iam::aws:policy/service-role/AWSLambdaBasicExecutionRole" +} + +# Creates a ZIP file containing the contents of the lambda directory which +# contains a Python script to calculate and put the custom metric +data "archive_file" "lambda_dms_replication_metric_zip" { + type = "zip" + source_dir = "${path.module}/lambda" + output_path = "${path.module}/lambda/dms_replication_metric.zip" + excludes = ["dms_replication_metric.zip"] +} + +# Define a Lambda Function using the python script in the ZIP file - +# we define the namespace of the custom metric (CustomDMSMetrics) +# and the Metric Name (DMSReplication Failure). The value of this +# metric is 0 if the replication task is not stopped (normal state), +# and 1 if not (whether it has been stopped manually or has failed) +resource "aws_lambda_function" "dms_replication_metric_publisher" { + function_name = "dms-replication-metric-publisher" + role = aws_iam_role.lambda_put_metric_data_role.arn + handler = "dms_replication_metric.lambda_handler" + runtime = "python3.8" + filename = data.archive_file.lambda_dms_replication_metric_zip.output_path + source_code_hash = data.archive_file.lambda_dms_replication_metric_zip.output_base64sha256 + environment { + variables = { + METRIC_NAMESPACE = "CustomDMSMetrics", + METRIC_NAME = "DMSReplicationFailure" + } + } + + depends_on = [data.archive_file.lambda_dms_replication_metric_zip] +} + +# Set Lambda function to allow the Replication Events call this functions +resource "aws_lambda_permission" "allow_sns_invoke_dms_replication_metric_publisher_handler" { + statement_id = "AllowSNSInvoke" + action = "lambda:InvokeFunction" + function_name = aws_lambda_function.dms_replication_metric_publisher.function_name + principal = "sns.amazonaws.com" + + source_arn = aws_sns_topic.dms_events_topic.arn +} + +resource "aws_cloudwatch_metric_alarm" "dms_replication_stopped_alarm" { + for_each = toset(local.replication_task_names) + alarm_name = "DMSReplicationStoppedAlarm_${each.key}" + alarm_description = "Alarm when Stopped Replication Task for ${each.key}" + comparison_operator = "GreaterThanThreshold" + evaluation_periods = 1 + threshold = 0 + treat_missing_data = "ignore" + datapoints_to_alarm = 1 + namespace = "CustomDMSMetrics" + metric_name = "DMSReplicationStopped" + statistic = "Maximum" + period = "60" + + dimensions = { + SourceId = each.key + } + + alarm_actions = [aws_sns_topic.dms_alerts_topic.arn] + ok_actions = [aws_sns_topic.dms_alerts_topic.arn] +} + + + +# SNS Topic for DMS replication events +# This is NOT the same as for DMS Cloudwatch Alarms (dms_alerting) +# and is used to trigger the Lamda function if an event happens during +# DMS Replication (Events are NOT detected by CloudWatch Alarms) +resource "aws_sns_topic" "dms_events_topic" { + name = "delius-dms-events-topic" + + lambda_success_feedback_role_arn = aws_iam_role.sns_logging_role.arn + lambda_success_feedback_sample_rate = 100 + lambda_failure_feedback_role_arn = aws_iam_role.sns_logging_role.arn +} + +resource "aws_sns_topic_subscription" "dms_events_lambda_subscription" { + topic_arn = aws_sns_topic.dms_events_topic.arn + protocol = "lambda" + endpoint = aws_lambda_function.dms_replication_metric_publisher.arn +} + +# We handle State Change and Failure DMS Events +resource "aws_dms_event_subscription" "dms_task_event_subscription" { + name = "dms-task-event-alerts" + sns_topic_arn = aws_sns_topic.dms_events_topic.arn + source_type = "replication-task" + event_categories = ["state change", "failure"] + enabled = true +} \ No newline at end of file diff --git a/terraform/environments/delius-core/modules/components/dms/lambda/dms_replication_metric.py b/terraform/environments/delius-core/modules/components/dms/lambda/dms_replication_metric.py new file mode 100644 index 00000000000..80195163a82 --- /dev/null +++ b/terraform/environments/delius-core/modules/components/dms/lambda/dms_replication_metric.py @@ -0,0 +1,69 @@ +import boto3 +import json +import logging +import re + +logger = logging.getLogger() +logger.setLevel(logging.INFO) + +def lambda_handler(event, context): + + cloudwatch = boto3.client('cloudwatch') + for record in event['Records']: + + message = json.loads(record['Sns']['Message']) + logger.info("SNS Message: %s",message) + + event_message = message.get("Event Message") + event_source = message.get("Event Source") + source_id = message.get("SourceId") + + dms_event_id = re.search(r"#(DMS-EVENT-\d+) $",message.get("Event ID")) + + # DMS Event IDs are documented at https://docs.aws.amazon.com/dms/latest/userguide/CHAP_Events.html + # + # Those relevant for this metric are: + # + # Running Replication: + # DMS-EVENT-0069: The replication task has started. + # DMS-EVENT-0081: A reload of table details has been requested. + # DMS-EVENT-0093: Reading resumed. + running_replication = ["DMS-EVENT-0069","DMS-EVENT-0081","DMS-EVENT-0093"] + # + # Stopped Replication: + # DMS-EVENT-0079: The replication task has stopped. + # DMS-EVENT-0091: Reading paused, swap files limit reached. + # DMS-EVENT-0092: Reading paused, disk usage limit reached. + # DMS-EVENT-0078: A replication task has failed. + stopped_replication = ["DMS-EVENT-0079","DMS-EVENT-0091","DMS-EVENT-0092","DMS-EVENT-0078"] + + if dms_event_id.group(1) in running_replication: + logger.info("TASK START: " + event_source + " task " + source_id + " started") + cloudwatch.put_metric_data( + Namespace='CustomDMSMetrics', + MetricData=[ + { + 'MetricName': 'DMSReplicationStopped', + 'Dimensions': [ + {'Name': 'SourceId', 'Value': source_id} + ], + 'Value': 0, # Reset Below Trigger threshold (Task Started) + 'Unit': 'Count' + } + ] + ) + elif dms_event_id.group(1) in stopped_replication: + logger.info("TASK STOPPED: " + event_source + " task " + source_id + " stopped") + cloudwatch.put_metric_data( + Namespace='CustomDMSMetrics', + MetricData=[ + { + 'MetricName': 'DMSReplicationStopped', + 'Dimensions': [ + {'Name': 'SourceId', 'Value': source_id} + ], + 'Value': 1, # Trigger threshold (Task Failed) + 'Unit': 'Count' + } + ] + ) \ No newline at end of file diff --git a/terraform/environments/delius-core/modules/components/dms/locals.tf b/terraform/environments/delius-core/modules/components/dms/locals.tf index 7d7b9f0a42a..1516a8e8441 100644 --- a/terraform/environments/delius-core/modules/components/dms/locals.tf +++ b/terraform/environments/delius-core/modules/components/dms/locals.tf @@ -41,4 +41,15 @@ locals { dms_s3_writer_role_name = "${var.env_name}-dms-s3-writer-role" dms_s3_reader_role_name = "${var.env_name}-dms-s3-reader-role" + replication_task_names = concat( + try([aws_dms_replication_task.user_inbound_replication[0].replication_task_id],[]), + try([aws_dms_replication_task.business_interaction_inbound_replication[0].replication_task_id],[]), + try([aws_dms_replication_task.audited_interaction_inbound_replication[0].replication_task_id],[]), + try([aws_dms_replication_task.audited_interaction_checksum_inbound_replication[0].replication_task_id],[]), + try([aws_dms_replication_task.audited_interaction_outbound_replication[0].replication_task_id],[]), + try([aws_dms_replication_task.business_interaction_outbound_replication[0].replication_task_id],[]), + try([aws_dms_replication_task.audited_interaction_outbound_replication[0].replication_task_id],[]), + try([aws_dms_replication_task.audited_interaction_checksum_outbound_replication[0].replication_task_id],[]) + ) + } \ No newline at end of file diff --git a/terraform/environments/delius-core/modules/delius_environment/ldap_ecs.tf b/terraform/environments/delius-core/modules/delius_environment/ldap_ecs.tf index 4e9b328bd62..b143dfe5e30 100644 --- a/terraform/environments/delius-core/modules/delius_environment/ldap_ecs.tf +++ b/terraform/environments/delius-core/modules/delius_environment/ldap_ecs.tf @@ -51,7 +51,7 @@ module "ldap_ecs" { container_image = "${var.platform_vars.environment_management.account_ids["core-shared-services-production"]}.dkr.ecr.eu-west-2.amazonaws.com/delius-core-openldap-ecr-repo:${var.delius_microservice_configs.ldap.image_tag}" account_config = var.account_config - health_check = { + container_health_check = { command = ["CMD-SHELL", "ldapsearch -x -H ldap://localhost:389 -b '' -s base '(objectclass=*)' namingContexts"] interval = 30 retries = 3 diff --git a/terraform/environments/delius-core/modules/delius_environment/pwm.tf b/terraform/environments/delius-core/modules/delius_environment/pwm.tf index 8a8af54987b..c5c6164fcbf 100644 --- a/terraform/environments/delius-core/modules/delius_environment/pwm.tf +++ b/terraform/environments/delius-core/modules/delius_environment/pwm.tf @@ -54,14 +54,22 @@ module "pwm" { platform_vars = var.platform_vars - container_image = "${var.platform_vars.environment_management.account_ids["core-shared-services-production"]}.dkr.ecr.eu-west-2.amazonaws.com/delius-core-password-management:${var.delius_microservice_configs.pwm.image_tag}" - account_config = var.account_config - health_check_path = "/" - health_check_interval = "15" - account_info = var.account_info - - target_group_protocol_version = "HTTP1" - health_check_grace_period_seconds = 10 + container_image = "${var.platform_vars.environment_management.account_ids["core-shared-services-production"]}.dkr.ecr.eu-west-2.amazonaws.com/delius-core-password-management:${var.delius_microservice_configs.pwm.image_tag}" + account_config = var.account_config + account_info = var.account_info + + target_group_protocol_version = "HTTP1" + + alb_health_check = { + path = "/" + healthy_threshold = 5 + interval = 30 + protocol = "HTTP" + unhealthy_threshold = 5 + matcher = "200-499" + timeout = 10 + grace_period_seconds = 180 + } container_cpu = var.delius_microservice_configs.pwm.container_cpu container_memory = var.delius_microservice_configs.pwm.container_memory diff --git a/terraform/environments/delius-core/modules/delius_environment/weblogic.tf b/terraform/environments/delius-core/modules/delius_environment/weblogic.tf index a9001a6f926..2d4314603c2 100644 --- a/terraform/environments/delius-core/modules/delius_environment/weblogic.tf +++ b/terraform/environments/delius-core/modules/delius_environment/weblogic.tf @@ -19,8 +19,20 @@ module "weblogic" { ecs_cluster_arn = module.ecs.ecs_cluster_arn env_name = var.env_name - health_check_path = "/NDelius-war/delius/JSP/healthcheck.jsp?ping" - microservice_lb = aws_lb.delius_core_frontend + pin_task_definition_revision = try(var.delius_microservice_configs.weblogic.task_definition_revision, 0) + + alb_health_check = { + path = "/NDelius-war/delius/JSP/healthcheck.jsp?ping" + healthy_threshold = 5 + interval = 30 + protocol = "HTTP" + unhealthy_threshold = 5 + matcher = "200-499" + timeout = 5 + grace_period_seconds = 300 + } + + microservice_lb = aws_lb.delius_core_frontend target_group_protocol_version = "HTTP1" diff --git a/terraform/environments/delius-core/modules/delius_environment/weblogic_eis.tf b/terraform/environments/delius-core/modules/delius_environment/weblogic_eis.tf index cd68c989724..ce08ba24080 100644 --- a/terraform/environments/delius-core/modules/delius_environment/weblogic_eis.tf +++ b/terraform/environments/delius-core/modules/delius_environment/weblogic_eis.tf @@ -68,9 +68,16 @@ module "weblogic_eis" { container_memory = var.delius_microservice_configs.weblogic_eis.container_memory container_cpu = var.delius_microservice_configs.weblogic_eis.container_cpu - health_check_path = "/NDelius-war/delius/JSP/healthcheck.jsp?ping" - health_check_grace_period_seconds = 600 - health_check_interval = 30 + alb_health_check = { + path = "/NDelius-war/delius/JSP/healthcheck.jsp?ping" + healthy_threshold = 5 + interval = 30 + protocol = "HTTP" + unhealthy_threshold = 5 + matcher = "200-499" + timeout = 10 + grace_period_seconds = 300 + } db_ingress_security_groups = [] diff --git a/terraform/environments/delius-core/modules/helpers/delius_microservice/ecs.tf b/terraform/environments/delius-core/modules/helpers/delius_microservice/ecs.tf index 6496b869f32..9fdfde284ab 100644 --- a/terraform/environments/delius-core/modules/helpers/delius_microservice/ecs.tf +++ b/terraform/environments/delius-core/modules/helpers/delius_microservice/ecs.tf @@ -1,5 +1,5 @@ module "container_definition" { - source = "git::https://github.com/ministryofjustice/modernisation-platform-terraform-ecs-cluster//container?ref=main" + source = "git::https://github.com/ministryofjustice/modernisation-platform-terraform-ecs-cluster//container?ref=v5.0.0" name = var.name image = var.container_image memory = var.container_memory @@ -9,7 +9,7 @@ module "container_definition" { environment = local.calculated_container_vars_list - health_check = var.health_check + health_check = var.container_health_check secrets = local.calculated_container_secrets_list port_mappings = var.container_port_config @@ -35,7 +35,7 @@ module "ecs_policies" { } module "ecs_service" { - source = "git::https://github.com/ministryofjustice/modernisation-platform-terraform-ecs-cluster//service?ref=main" + source = "git::https://github.com/ministryofjustice/modernisation-platform-terraform-ecs-cluster//service?ref=v5.0.0" container_definitions = nonsensitive(module.container_definition.json_encoded_list) cluster_arn = var.ecs_cluster_arn name = "${var.env_name}-${var.name}" @@ -43,6 +43,8 @@ module "ecs_service" { task_cpu = var.container_cpu task_memory = var.container_memory + pin_task_definition_revision = var.pin_task_definition_revision + desired_count = var.desired_count deployment_maximum_percent = var.deployment_maximum_percent deployment_minimum_healthy_percent = var.deployment_minimum_healthy_percent @@ -51,7 +53,7 @@ module "ecs_service" { task_role_arn = "arn:aws:iam::${var.account_info.id}:role/${module.ecs_policies.task_role.name}" task_exec_role_arn = "arn:aws:iam::${var.account_info.id}:role/${module.ecs_policies.task_exec_role.name}" - health_check_grace_period_seconds = var.health_check_grace_period_seconds + health_check_grace_period_seconds = var.alb_health_check.grace_period_seconds service_load_balancers = var.microservice_lb != null ? concat([{ target_group_arn = aws_lb_target_group.frontend[0].arn @@ -68,7 +70,5 @@ module "ecs_service" { enable_execute_command = true - ignore_changes = var.ignore_changes_service_task_definition - tags = var.tags } diff --git a/terraform/environments/delius-core/modules/helpers/delius_microservice/load_balancing.tf b/terraform/environments/delius-core/modules/helpers/delius_microservice/load_balancing.tf index 7f46a8cd665..90019fa63f9 100644 --- a/terraform/environments/delius-core/modules/helpers/delius_microservice/load_balancing.tf +++ b/terraform/environments/delius-core/modules/helpers/delius_microservice/load_balancing.tf @@ -28,13 +28,13 @@ resource "aws_lb_target_group" "frontend" { } health_check { - path = var.health_check_path - healthy_threshold = "5" - interval = var.health_check_interval - protocol = "HTTP" - unhealthy_threshold = "5" - matcher = "200-499" - timeout = "5" + path = var.alb_health_check.path + healthy_threshold = var.alb_health_check.healthy_threshold + interval = var.alb_health_check.interval + protocol = var.alb_health_check.protocol + unhealthy_threshold = var.alb_health_check.unhealthy_threshold + matcher = var.alb_health_check.matcher + timeout = var.alb_health_check.timeout } lifecycle { diff --git a/terraform/environments/delius-core/modules/helpers/delius_microservice/variables.tf b/terraform/environments/delius-core/modules/helpers/delius_microservice/variables.tf index 8676dcb62ed..93ac9c86fe9 100644 --- a/terraform/environments/delius-core/modules/helpers/delius_microservice/variables.tf +++ b/terraform/environments/delius-core/modules/helpers/delius_microservice/variables.tf @@ -217,12 +217,6 @@ variable "platform_vars" { }) } -variable "health_check_grace_period_seconds" { - description = "The amount of time, in seconds, that Amazon ECS waits before unhealthy instances are shut down." - type = number - default = 60 -} - variable "ecs_cluster_arn" { description = "The ARN of the ECS cluster" type = string @@ -376,18 +370,6 @@ variable "alb_security_group_id" { default = null } -variable "health_check_path" { - description = "The health check path for the alb target group" - type = string - default = "/" -} - -variable "health_check_interval" { - description = "The health check interval for the alb target group" - type = string - default = "300" -} - variable "alb_stickiness_enabled" { description = "Enable or disable stickiness" type = string @@ -581,7 +563,7 @@ variable "extra_task_exec_role_policies" { default = {} } -variable "health_check" { +variable "container_health_check" { description = "The health check configuration for the container" type = object({ command = list(string) @@ -593,6 +575,30 @@ variable "health_check" { default = null } +variable "alb_health_check" { + description = "The health check configuration for the ALB" + type = object({ + path = string + interval = number + timeout = number + healthy_threshold = number + unhealthy_threshold = number + matcher = string + protocol = string + grace_period_seconds = number + }) + default = { + path = "/" + interval = 30 + timeout = 5 + healthy_threshold = 5 + unhealthy_threshold = 5 + matcher = "200-499" + protocol = "HTTP" + grace_period_seconds = 120 + } +} + variable "nlb_ingress_security_group_ids" { description = "Security group ids to allow ingress to the ECS service" type = list(object({ @@ -622,3 +628,9 @@ variable "system_controls" { type = list(any) default = [] } + +variable "pin_task_definition_revision" { + type = number + description = "The revision of the task definition to use" + default = 0 +} diff --git a/terraform/environments/digital-prison-reporting/application_variables.json b/terraform/environments/digital-prison-reporting/application_variables.json index 5ef69277647..18c80c7cd78 100644 --- a/terraform/environments/digital-prison-reporting/application_variables.json +++ b/terraform/environments/digital-prison-reporting/application_variables.json @@ -94,10 +94,7 @@ "setup_sonatype_secrets": true, "setup_scheduled_action_iam_role": true, "setup_redshift_schedule": true, - "dps_domains": [ - "dps-activities", - "dps-case-notes" - ], + "dps_domains": ["dps-activities", "dps-case-notes"], "alarms": { "setup_cw_alarms": true, "redshift": { @@ -272,10 +269,7 @@ "setup_sonatype_secrets": false, "setup_scheduled_action_iam_role": true, "setup_redshift_schedule": true, - "dps_domains": [ - "dps-activities", - "dps-case-notes" - ], + "dps_domains": ["dps-activities", "dps-case-notes"], "alarms": { "setup_cw_alarms": true, "redshift": { @@ -452,10 +446,7 @@ "setup_scheduled_action_iam_role": true, "setup_redshift_schedule": true, "enable_redshift_health_check": true, - "dps_domains": [ - "dps-activities", - "dps-case-notes" - ], + "dps_domains": ["dps-activities", "dps-case-notes"], "alarms": { "setup_cw_alarms": true, "redshift": { @@ -648,10 +639,7 @@ "setup_sonatype_secrets": false, "setup_scheduled_action_iam_role": false, "setup_redshift_schedule": false, - "dps_domains": [ - "dps-activities", - "dps-case-notes" - ], + "dps_domains": ["dps-activities", "dps-case-notes"], "alarms": { "setup_cw_alarms": true, "redshift": { diff --git a/terraform/environments/digital-prison-reporting/modules/dms_s3_v2/versions.tf b/terraform/environments/digital-prison-reporting/modules/dms_s3_v2/versions.tf index d2163a87985..bf68a137672 100644 --- a/terraform/environments/digital-prison-reporting/modules/dms_s3_v2/versions.tf +++ b/terraform/environments/digital-prison-reporting/modules/dms_s3_v2/versions.tf @@ -6,7 +6,7 @@ terraform { } template = { - source = "hashicorp/template" + source = "hashicorp/template" version = "~> 2.2" } diff --git a/terraform/environments/digital-prison-reporting/modules/domains/dms-endpoints/variables.tf b/terraform/environments/digital-prison-reporting/modules/domains/dms-endpoints/variables.tf index aa7f9023442..a2dc57b6c98 100644 --- a/terraform/environments/digital-prison-reporting/modules/domains/dms-endpoints/variables.tf +++ b/terraform/environments/digital-prison-reporting/modules/domains/dms-endpoints/variables.tf @@ -117,7 +117,7 @@ variable "identifier" { #-------------------------------------------------------------- variable "target_backup_retention_period" { - type = string + type = string # Days default = "30" description = "Retention of RDS backups" diff --git a/terraform/environments/digital-prison-reporting/modules/s3_bucket/main.tf b/terraform/environments/digital-prison-reporting/modules/s3_bucket/main.tf index 4cf22f0992a..50c941d071e 100644 --- a/terraform/environments/digital-prison-reporting/modules/s3_bucket/main.tf +++ b/terraform/environments/digital-prison-reporting/modules/s3_bucket/main.tf @@ -46,7 +46,7 @@ resource "aws_s3_bucket_lifecycle_configuration" "lifecycle" { # - Transitions objects to STANDARD_IA after 30 days (cost-effective storage for infrequent access). # - Deletes objects after 90 days. dynamic "transition" { - for_each = var.lifecycle_category == "short_term" ? [ { days = 30, storage_class = "STANDARD_IA" } ] : [] + for_each = var.lifecycle_category == "short_term" ? [{ days = 30, storage_class = "STANDARD_IA" }] : [] content { days = transition.value.days storage_class = transition.value.storage_class @@ -54,8 +54,8 @@ resource "aws_s3_bucket_lifecycle_configuration" "lifecycle" { } dynamic "expiration" { - for_each = var.lifecycle_category == "short_term" ? [ { days = 90 } ] : ( - var.lifecycle_category == "temporary" ? [ { days = 30 } ] : []) + for_each = var.lifecycle_category == "short_term" ? [{ days = 90 }] : ( + var.lifecycle_category == "temporary" ? [{ days = 30 }] : []) content { days = expiration.value.days } diff --git a/terraform/environments/edw/ec2.tf b/terraform/environments/edw/ec2.tf index 73dd891836b..67aab63cb4d 100644 --- a/terraform/environments/edw/ec2.tf +++ b/terraform/environments/edw/ec2.tf @@ -347,7 +347,7 @@ EOF ####### IAM role ####### resource "aws_iam_role" "edw_ec2_role" { - name = "${local.application_name}-ec2-instance-role" + name = "${local.application_name}-ec2-instance-role" tags = merge( local.tags, { diff --git a/terraform/environments/electronic-monitoring-data/modules/landing_bucket/main.tf b/terraform/environments/electronic-monitoring-data/modules/landing_bucket/main.tf index 1438a386f02..3560af40d81 100644 --- a/terraform/environments/electronic-monitoring-data/modules/landing_bucket/main.tf +++ b/terraform/environments/electronic-monitoring-data/modules/landing_bucket/main.tf @@ -77,6 +77,46 @@ module "this-bucket" { ) } +#----------------------------------------------------------------------------------- +# KMS - customer managed key for use with cross account data +#----------------------------------------------------------------------------------- + +module "kms_key" { + #checkov:skip=CKV_TF_1:Module registry does not support commit hashes for versions + #checkov:skip=CKV_TF_2:Module registry does not support tags for versions + + source = "terraform-aws-modules/kms/aws" + version = "3.1.1" + + aliases = ["s3/landing_bucket_${var.data_feed}_${var.order_type}"] + description = "${var.data_feed} ${var.order_type} landing bucket KMS key" + + # Give full access to key for root account, and lambda role ability to use. + enable_default_policy = true + key_users = [aws_iam_role.process_landing_bucket_files.arn] + + deletion_window_in_days = 7 + + # Grant external account role specific operations. + # To view grants, need to use cli: + # aws kms list-grants --region=eu-west-2 --key-id + grants = var.cross_account_access_role != null ? { + cross_account_access_role = { + grantee_principal = "arn:aws:iam::${var.cross_account_access_role.account_number}:role/${var.cross_account_access_role.role_name}" + operations = [ + "Encrypt", + "GenerateDataKey", + ] + } + } : {} + + tags = merge( + var.local_tags, + { order_type = var.order_type }, + { data_feed = var.data_feed } + ) +} + #----------------------------------------------------------------------------------- # Process landing bucket files - lambda triggers #----------------------------------------------------------------------------------- @@ -155,6 +195,17 @@ data "aws_iam_policy_document" "process_landing_bucket_files_s3_policy_document" "arn:aws:s3:::${var.received_files_bucket_id}/*", ] } + + statement { + sid = "KMSDecryptObjects" + effect = "Allow" + actions = [ + "kms:Decrypt", + ] + resources = [ + module.kms_key.key_arn, + ] + } } resource "aws_iam_policy" "process_landing_bucket_files_s3" { diff --git a/terraform/environments/electronic-monitoring-data/s3.tf b/terraform/environments/electronic-monitoring-data/s3.tf index b9d23c5236b..a35631c8a09 100644 --- a/terraform/environments/electronic-monitoring-data/s3.tf +++ b/terraform/environments/electronic-monitoring-data/s3.tf @@ -5,15 +5,15 @@ locals { "production" = null "preproduction" = { "account_number" = 173142358744 - "role_name" = "juniper-datatransfer-lambda-role" + "role_name" = "juniper-datatransfer-lambda-role" } "test" = { "account_number" = 173142358744 - "role_name" = "dev-datatransfer-lambda-role" + "role_name" = "dev-datatransfer-lambda-role" } "development" = { "account_number" = 173142358744 - "role_name" = "dev-datatransfer-lambda-role" + "role_name" = "dev-datatransfer-lambda-role" } } } diff --git a/terraform/environments/hmpps-domain-services/locals.tf b/terraform/environments/hmpps-domain-services/locals.tf index d6e5ed777f9..b06f71f4ad6 100644 --- a/terraform/environments/hmpps-domain-services/locals.tf +++ b/terraform/environments/hmpps-domain-services/locals.tf @@ -24,6 +24,7 @@ locals { "lb", "ec2", "ec2_windows", + "ssm_command", ] cloudwatch_metric_alarms_default_actions = ["pagerduty"] cloudwatch_metric_oam_links_ssm_parameters = ["hmpps-oem-${local.environment}"] @@ -38,6 +39,7 @@ locals { enable_hmpps_domain = true enable_image_builder = true enable_s3_bucket = true + enable_ssm_command_monitoring = true s3_iam_policies = ["EC2S3BucketWriteAndDeleteAccessPolicy"] } } diff --git a/terraform/environments/hmpps-domain-services/locals_preproduction.tf b/terraform/environments/hmpps-domain-services/locals_preproduction.tf index bb72afcc886..33544ecf6b1 100644 --- a/terraform/environments/hmpps-domain-services/locals_preproduction.tf +++ b/terraform/environments/hmpps-domain-services/locals_preproduction.tf @@ -153,9 +153,10 @@ locals { }) } - schedule_alarms = { + schedule_alarms_lambda = { + function_name = "schedule-alarms" alarm_patterns = [ - "public-https-*-https-unhealthy-load-balancer-host", + "public-https-*-unhealthy-load-balancer-host", ] } diff --git a/terraform/environments/hmpps-domain-services/locals_secretsmanager.tf b/terraform/environments/hmpps-domain-services/locals_secretsmanager.tf index 0273edb078c..9a1ccda3578 100644 --- a/terraform/environments/hmpps-domain-services/locals_secretsmanager.tf +++ b/terraform/environments/hmpps-domain-services/locals_secretsmanager.tf @@ -9,8 +9,6 @@ locals { "arn:aws:iam::${module.environment.account_ids.corporate-staff-rostering-test}:role/EC2HmppsDomainSecretsRole", "arn:aws:iam::${module.environment.account_ids.planetfm-development}:role/EC2HmppsDomainSecretsRole", "arn:aws:iam::${module.environment.account_ids.planetfm-test}:role/EC2HmppsDomainSecretsRole", - "arn:aws:iam::${module.environment.account_ids.corporate-staff-rostering-development}:role/LambdaFunctionADObjectCleanUp", - "arn:aws:iam::${module.environment.account_ids.corporate-staff-rostering-test}:role/LambdaFunctionADObjectCleanUp", "arn:aws:iam::${module.environment.account_ids.core-shared-services-production}:role/ad-fixngo-ec2-nonlive-role", "arn:aws:iam::${module.environment.account_ids.nomis-development}:role/EC2HmppsDomainSecretsRole", "arn:aws:iam::${module.environment.account_ids.nomis-test}:role/EC2HmppsDomainSecretsRole", @@ -26,8 +24,6 @@ locals { "arn:aws:iam::${module.environment.account_ids.corporate-staff-rostering-production}:role/EC2HmppsDomainSecretsRole", "arn:aws:iam::${module.environment.account_ids.planetfm-preproduction}:role/EC2HmppsDomainSecretsRole", "arn:aws:iam::${module.environment.account_ids.planetfm-production}:role/EC2HmppsDomainSecretsRole", - "arn:aws:iam::${module.environment.account_ids.corporate-staff-rostering-preproduction}:role/LambdaFunctionADObjectCleanUp", - "arn:aws:iam::${module.environment.account_ids.corporate-staff-rostering-production}:role/LambdaFunctionADObjectCleanUp", "arn:aws:iam::${module.environment.account_ids.core-shared-services-production}:role/ad-fixngo-ec2-live-role", "arn:aws:iam::${module.environment.account_ids.nomis-preproduction}:role/EC2HmppsDomainSecretsRole", "arn:aws:iam::${module.environment.account_ids.nomis-production}:role/EC2HmppsDomainSecretsRole", diff --git a/terraform/environments/hmpps-domain-services/main.tf b/terraform/environments/hmpps-domain-services/main.tf index 04db08b1919..d234a1e3a07 100644 --- a/terraform/environments/hmpps-domain-services/main.tf +++ b/terraform/environments/hmpps-domain-services/main.tf @@ -74,6 +74,7 @@ module "baseline" { ) cloudwatch_metric_alarms = merge( + module.baseline_presets.cloudwatch_metric_alarms_baseline, lookup(local.baseline_all_environments, "cloudwatch_metric_alarms", {}), lookup(local.baseline_environment_specific, "cloudwatch_metric_alarms", {}), ) @@ -178,11 +179,8 @@ module "baseline" { ) schedule_alarms_lambda = merge( - { - function_name = "schedule-alarms" - }, - lookup(local.baseline_all_environments, "schedule_alarms", {}), - lookup(local.baseline_environment_specific, "schedule_alarms", {}), + lookup(local.baseline_all_environments, "schedule_alarms_lambda", {}), + lookup(local.baseline_environment_specific, "schedule_alarms_lambda", {}), ) secretsmanager_secrets = merge( diff --git a/terraform/environments/hmpps-oem/locals.tf b/terraform/environments/hmpps-oem/locals.tf index 73791385114..73264f3b4cb 100644 --- a/terraform/environments/hmpps-oem/locals.tf +++ b/terraform/environments/hmpps-oem/locals.tf @@ -19,6 +19,7 @@ locals { baseline_environment_specific = local.baseline_environments_specific[local.environment] cloudwatch_dashboard_default_widget_groups = [ + "ec2_instance_endpoint_monitoring", "network_lb", "lb", "ec2", @@ -49,6 +50,8 @@ locals { enable_s3_db_backup_bucket = true enable_s3_shared_bucket = true enable_s3_software_bucket = true + enable_ssm_command_monitoring = true + enable_ssm_missing_metric_monitoring = true s3_iam_policies = ["EC2S3BucketWriteAndDeleteAccessPolicy"] } } @@ -71,6 +74,16 @@ locals { module.baseline_presets.cloudwatch_dashboard_widget_groups.ec2_instance_oracle_db_with_backup, ] } + "endpoints-and-pipelines" = { + account_name = "hmpps-oem-${local.environment}" + periodOverride = "auto" + start = "-PT6H" + widget_groups = [ + module.baseline_presets.cloudwatch_dashboard_widget_groups.ec2_instance_endpoint_monitoring, + module.baseline_presets.cloudwatch_dashboard_widget_groups.ssm_command, + module.baseline_presets.cloudwatch_dashboard_widget_groups.github_workflows, + ] + } "hmpps-domain-services-${local.environment}" = { account_name = "hmpps-domain-services-${local.environment}" periodOverride = "auto" @@ -82,16 +95,37 @@ locals { ] } "hmpps-oem-${local.environment}" = { - account_name = "hmpps-oem-${local.environment}" + account_name = null periodOverride = "auto" start = "-PT6H" - widget_groups = [ - module.baseline_presets.cloudwatch_dashboard_widget_groups.ec2, - module.baseline_presets.cloudwatch_dashboard_widget_groups.ec2_linux, - module.baseline_presets.cloudwatch_dashboard_widget_groups.ec2_instance_linux, - module.baseline_presets.cloudwatch_dashboard_widget_groups.ec2_instance_oracle_db_with_backup, - module.baseline_presets.cloudwatch_dashboard_widget_groups.ec2_instance_textfile_monitoring, - ] + widget_groups = [{ + header_markdown = "## EC2 Oracle Enterprise Management" + width = 8 + height = 8 + add_ebs_widgets = { + iops = true + throughput = true + } + search_filter = { + ec2_tag = [ + { tag_name = "server-type", tag_value = "hmpps-oem" }, + ] + } + widgets = [ + module.baseline_presets.cloudwatch_dashboard_widgets.ec2.cpu-utilization-high, + module.baseline_presets.cloudwatch_dashboard_widgets.ec2.instance-status-check-failed, + module.baseline_presets.cloudwatch_dashboard_widgets.ec2.system-status-check-failed, + module.baseline_presets.cloudwatch_dashboard_widgets.ec2_cwagent_linux.free-disk-space-low, + module.baseline_presets.cloudwatch_dashboard_widgets.ec2_cwagent_linux.high-memory-usage, + module.baseline_presets.cloudwatch_dashboard_widgets.ec2_cwagent_linux.cpu-iowait-high, + module.baseline_presets.cloudwatch_dashboard_widgets.ec2_instance_cwagent_linux.free-disk-space-low, + module.baseline_presets.cloudwatch_dashboard_widgets.ec2_instance_cwagent_collectd_service_status_os.service-status-error-os-layer, + module.baseline_presets.cloudwatch_dashboard_widgets.ec2_instance_cwagent_collectd_service_status_app.service-status-error-app-layer, + module.baseline_presets.cloudwatch_dashboard_widgets.ec2_instance_cwagent_collectd_oracle_db_connected.oracle-db-disconnected, + module.baseline_presets.cloudwatch_dashboard_widgets.ec2_instance_cwagent_collectd_oracle_db_backup.oracle-db-rman-backup-error, + module.baseline_presets.cloudwatch_dashboard_widgets.ec2_instance_cwagent_collectd_oracle_db_backup.oracle-db-rman-backup-did-not-run, + ] + }] } "nomis-${local.environment}" = { account_name = "nomis-${local.environment}" diff --git a/terraform/environments/hmpps-oem/locals_cloudwatch_metric_alarms.tf b/terraform/environments/hmpps-oem/locals_cloudwatch_metric_alarms.tf index 0bbea69f30f..9921b4b0510 100644 --- a/terraform/environments/hmpps-oem/locals_cloudwatch_metric_alarms.tf +++ b/terraform/environments/hmpps-oem/locals_cloudwatch_metric_alarms.tf @@ -25,7 +25,6 @@ locals { csr-r4-pp = ["r4.pp.csr.service.justice.gov.uk", false, "corporate-staff-rostering-pagerduty"] csr-r5-pp = ["r5.pp.csr.service.justice.gov.uk", false, "corporate-staff-rostering-pagerduty"] csr-r6-pp = ["r6.pp.csr.service.justice.gov.uk", false, "corporate-staff-rostering-pagerduty"] - hpa-preprod = ["hpa-preprod.service.hmpps.dsd.io", true, "azure-fixngo-pagerduty"] nomis-lsast = ["c.lsast-nomis.az.justice.gov.uk", true, "nomis-pagerduty"] nomis-pp = ["c.pp-nomis.az.justice.gov.uk", true, "nomis-pagerduty"] nomis-reporting-pp = ["reporting.pp-nomis.az.justice.gov.uk", true, "nomis-combined-reporting-pagerduty"] diff --git a/terraform/environments/hmpps-oem/main.tf b/terraform/environments/hmpps-oem/main.tf index a23f7e6d41b..d234a1e3a07 100644 --- a/terraform/environments/hmpps-oem/main.tf +++ b/terraform/environments/hmpps-oem/main.tf @@ -74,6 +74,7 @@ module "baseline" { ) cloudwatch_metric_alarms = merge( + module.baseline_presets.cloudwatch_metric_alarms_baseline, lookup(local.baseline_all_environments, "cloudwatch_metric_alarms", {}), lookup(local.baseline_environment_specific, "cloudwatch_metric_alarms", {}), ) @@ -177,6 +178,11 @@ module "baseline" { lookup(local.baseline_environment_specific, "s3_buckets", {}), ) + schedule_alarms_lambda = merge( + lookup(local.baseline_all_environments, "schedule_alarms_lambda", {}), + lookup(local.baseline_environment_specific, "schedule_alarms_lambda", {}), + ) + secretsmanager_secrets = merge( module.baseline_presets.secretsmanager_secrets, lookup(local.baseline_all_environments, "secretsmanager_secrets", {}), diff --git a/terraform/environments/nomis-combined-reporting/locals.tf b/terraform/environments/nomis-combined-reporting/locals.tf index 68b4c09eeca..3acc84b9578 100644 --- a/terraform/environments/nomis-combined-reporting/locals.tf +++ b/terraform/environments/nomis-combined-reporting/locals.tf @@ -27,6 +27,7 @@ locals { "ec2_instance_linux", "ec2_instance_oracle_db_with_backup", "ec2_windows", + "ssm_command", ] cloudwatch_metric_alarms_default_actions = ["pagerduty"] cloudwatch_metric_oam_links_ssm_parameters = ["hmpps-oem-${local.environment}"] @@ -44,6 +45,7 @@ locals { enable_s3_bucket = true enable_s3_db_backup_bucket = true enable_s3_software_bucket = true + enable_ssm_command_monitoring = true s3_iam_policies = ["EC2S3BucketWriteAndDeleteAccessPolicy"] } } diff --git a/terraform/environments/nomis-combined-reporting/main.tf b/terraform/environments/nomis-combined-reporting/main.tf index a23f7e6d41b..d234a1e3a07 100644 --- a/terraform/environments/nomis-combined-reporting/main.tf +++ b/terraform/environments/nomis-combined-reporting/main.tf @@ -74,6 +74,7 @@ module "baseline" { ) cloudwatch_metric_alarms = merge( + module.baseline_presets.cloudwatch_metric_alarms_baseline, lookup(local.baseline_all_environments, "cloudwatch_metric_alarms", {}), lookup(local.baseline_environment_specific, "cloudwatch_metric_alarms", {}), ) @@ -177,6 +178,11 @@ module "baseline" { lookup(local.baseline_environment_specific, "s3_buckets", {}), ) + schedule_alarms_lambda = merge( + lookup(local.baseline_all_environments, "schedule_alarms_lambda", {}), + lookup(local.baseline_environment_specific, "schedule_alarms_lambda", {}), + ) + secretsmanager_secrets = merge( module.baseline_presets.secretsmanager_secrets, lookup(local.baseline_all_environments, "secretsmanager_secrets", {}), diff --git a/terraform/environments/nomis-data-hub/locals.tf b/terraform/environments/nomis-data-hub/locals.tf index 794da1cd6bf..2ca908e66c0 100644 --- a/terraform/environments/nomis-data-hub/locals.tf +++ b/terraform/environments/nomis-data-hub/locals.tf @@ -26,6 +26,7 @@ locals { "ec2_instance_linux", "ec2_instance_textfile_monitoring", "ec2_windows", + "ssm_command", ] cloudwatch_metric_alarms_default_actions = ["pagerduty"] cloudwatch_metric_oam_links_ssm_parameters = ["hmpps-oem-${local.environment}"] @@ -42,6 +43,7 @@ locals { enable_image_builder = true enable_s3_bucket = true enable_s3_software_bucket = true + enable_ssm_command_monitoring = true s3_iam_policies = ["EC2S3BucketWriteAndDeleteAccessPolicy"] } } diff --git a/terraform/environments/nomis-data-hub/main.tf b/terraform/environments/nomis-data-hub/main.tf index a23f7e6d41b..d234a1e3a07 100644 --- a/terraform/environments/nomis-data-hub/main.tf +++ b/terraform/environments/nomis-data-hub/main.tf @@ -74,6 +74,7 @@ module "baseline" { ) cloudwatch_metric_alarms = merge( + module.baseline_presets.cloudwatch_metric_alarms_baseline, lookup(local.baseline_all_environments, "cloudwatch_metric_alarms", {}), lookup(local.baseline_environment_specific, "cloudwatch_metric_alarms", {}), ) @@ -177,6 +178,11 @@ module "baseline" { lookup(local.baseline_environment_specific, "s3_buckets", {}), ) + schedule_alarms_lambda = merge( + lookup(local.baseline_all_environments, "schedule_alarms_lambda", {}), + lookup(local.baseline_environment_specific, "schedule_alarms_lambda", {}), + ) + secretsmanager_secrets = merge( module.baseline_presets.secretsmanager_secrets, lookup(local.baseline_all_environments, "secretsmanager_secrets", {}), diff --git a/terraform/environments/nomis/locals.tf b/terraform/environments/nomis/locals.tf index ada318258c9..2f5c27143a0 100644 --- a/terraform/environments/nomis/locals.tf +++ b/terraform/environments/nomis/locals.tf @@ -37,6 +37,7 @@ locals { enable_s3_bucket = true enable_s3_db_backup_bucket = true enable_s3_software_bucket = true + enable_ssm_command_monitoring = true route53_resolver_rules = { outbound-data-and-private-subnets = ["azure-fixngo-domain"] } s3_iam_policies = ["EC2S3BucketWriteAndDeleteAccessPolicy"] software_bucket_name = "ec2-image-builder-nomis" @@ -48,7 +49,6 @@ locals { enable_resource_explorer = true } - cloudwatch_metric_alarms = module.baseline_presets.cloudwatch_metric_alarms.ssm - security_groups = local.security_groups + security_groups = local.security_groups } } diff --git a/terraform/environments/nomis/main.tf b/terraform/environments/nomis/main.tf index a23f7e6d41b..d234a1e3a07 100644 --- a/terraform/environments/nomis/main.tf +++ b/terraform/environments/nomis/main.tf @@ -74,6 +74,7 @@ module "baseline" { ) cloudwatch_metric_alarms = merge( + module.baseline_presets.cloudwatch_metric_alarms_baseline, lookup(local.baseline_all_environments, "cloudwatch_metric_alarms", {}), lookup(local.baseline_environment_specific, "cloudwatch_metric_alarms", {}), ) @@ -177,6 +178,11 @@ module "baseline" { lookup(local.baseline_environment_specific, "s3_buckets", {}), ) + schedule_alarms_lambda = merge( + lookup(local.baseline_all_environments, "schedule_alarms_lambda", {}), + lookup(local.baseline_environment_specific, "schedule_alarms_lambda", {}), + ) + secretsmanager_secrets = merge( module.baseline_presets.secretsmanager_secrets, lookup(local.baseline_all_environments, "secretsmanager_secrets", {}), diff --git a/terraform/environments/oasys-national-reporting/locals.tf b/terraform/environments/oasys-national-reporting/locals.tf index c8298624dd8..05c0cc63abe 100644 --- a/terraform/environments/oasys-national-reporting/locals.tf +++ b/terraform/environments/oasys-national-reporting/locals.tf @@ -26,6 +26,7 @@ locals { "ec2_linux", "ec2_instance_linux", "ec2_windows", + "ssm_command", ] cloudwatch_metric_alarms_default_actions = ["pagerduty"] cloudwatch_metric_oam_links_ssm_parameters = ["hmpps-oem-${local.environment}"] @@ -41,6 +42,7 @@ locals { enable_image_builder = true enable_s3_bucket = true enable_s3_shared_bucket = true + enable_ssm_command_monitoring = true s3_iam_policies = ["EC2S3BucketWriteAndDeleteAccessPolicy"] } } diff --git a/terraform/environments/oasys-national-reporting/locals_test.tf b/terraform/environments/oasys-national-reporting/locals_test.tf index 096a578a62d..cfc069059fc 100644 --- a/terraform/environments/oasys-national-reporting/locals_test.tf +++ b/terraform/environments/oasys-national-reporting/locals_test.tf @@ -133,10 +133,10 @@ locals { instance_profile_policies = concat(local.ec2_autoscaling_groups.bods.config.instance_profile_policies, [ "Ec2SecretPolicy", ]) - # user_data_raw = base64encode(templatefile( - # "./templates/user-data-onr-bods-pwsh.yaml.tftpl", { - # branch = "TM/TM-620/test-pagefile-change" - # })) + user_data_raw = base64encode(templatefile( + "./templates/user-data-onr-bods-pwsh.yaml.tftpl", { + branch = "TM/TM-660/onr-bods-second-server" + })) }) instance = merge(local.ec2_autoscaling_groups.bods.instance, { instance_type = "m4.xlarge" @@ -151,26 +151,22 @@ locals { ec2_instances = { - # t2-onr-bods-1 = merge(local.ec2_instances.bods, { - # config = merge(local.ec2_instances.bods.config, { - # availability_zone = "eu-west-2a" - # user_data_raw = base64encode(templatefile( - # "./templates/user-data-onr-bods-pwsh.yaml.tftpl", { - # } - # )) - # instance_profile_policies = concat(local.ec2_instances.bods.config.instance_profile_policies, [ - # "Ec2SecretPolicy", - # ]) - # }) - # instance = merge(local.ec2_instances.bods.instance, { - # instance_type = "m4.xlarge" - # }) - # cloudwatch_metric_alarms = null - # tags = merge(local.ec2_instances.bods.tags, { - # oasys-national-reporting-environment = "t2" - # domain-name = "azure.noms.root" - # }) - # }) + t2-onr-bods-1 = merge(local.ec2_instances.bods, { + config = merge(local.ec2_instances.bods.config, { + availability_zone = "eu-west-2a" + instance_profile_policies = concat(local.ec2_instances.bods.config.instance_profile_policies, [ + "Ec2SecretPolicy", + ]) + }) + instance = merge(local.ec2_instances.bods.instance, { + instance_type = "m4.xlarge" + }) + cloudwatch_metric_alarms = null + tags = merge(local.ec2_instances.bods.tags, { + oasys-national-reporting-environment = "t2" + domain-name = "azure.noms.root" + }) + }) # Pending sorting out cluster install of Bods in modernisation-platform-configuration-management repo # t2-onr-bods-2 = merge(local.ec2_instances.bods, { diff --git a/terraform/environments/oasys-national-reporting/main.tf b/terraform/environments/oasys-national-reporting/main.tf index a23f7e6d41b..d234a1e3a07 100644 --- a/terraform/environments/oasys-national-reporting/main.tf +++ b/terraform/environments/oasys-national-reporting/main.tf @@ -74,6 +74,7 @@ module "baseline" { ) cloudwatch_metric_alarms = merge( + module.baseline_presets.cloudwatch_metric_alarms_baseline, lookup(local.baseline_all_environments, "cloudwatch_metric_alarms", {}), lookup(local.baseline_environment_specific, "cloudwatch_metric_alarms", {}), ) @@ -177,6 +178,11 @@ module "baseline" { lookup(local.baseline_environment_specific, "s3_buckets", {}), ) + schedule_alarms_lambda = merge( + lookup(local.baseline_all_environments, "schedule_alarms_lambda", {}), + lookup(local.baseline_environment_specific, "schedule_alarms_lambda", {}), + ) + secretsmanager_secrets = merge( module.baseline_presets.secretsmanager_secrets, lookup(local.baseline_all_environments, "secretsmanager_secrets", {}), diff --git a/terraform/environments/oasys/locals.tf b/terraform/environments/oasys/locals.tf index 150199c622a..21c2dff9d24 100644 --- a/terraform/environments/oasys/locals.tf +++ b/terraform/environments/oasys/locals.tf @@ -28,6 +28,7 @@ locals { "ec2_instance_linux", "ec2_instance_oracle_db_with_backup", "ec2_instance_textfile_monitoring", + "ssm_command", ] cloudwatch_metric_alarms_default_actions = ["pagerduty"] cloudwatch_metric_oam_links_ssm_parameters = ["hmpps-oem-${local.environment}"] @@ -45,6 +46,7 @@ locals { enable_s3_bucket = true enable_s3_db_backup_bucket = true enable_s3_shared_bucket = true + enable_ssm_command_monitoring = true enable_vmimport = true s3_bucket_name = "${local.application_name}-${local.environment}" s3_iam_policies = ["EC2S3BucketWriteAndDeleteAccessPolicy"] diff --git a/terraform/environments/oasys/main.tf b/terraform/environments/oasys/main.tf index a23f7e6d41b..d234a1e3a07 100644 --- a/terraform/environments/oasys/main.tf +++ b/terraform/environments/oasys/main.tf @@ -74,6 +74,7 @@ module "baseline" { ) cloudwatch_metric_alarms = merge( + module.baseline_presets.cloudwatch_metric_alarms_baseline, lookup(local.baseline_all_environments, "cloudwatch_metric_alarms", {}), lookup(local.baseline_environment_specific, "cloudwatch_metric_alarms", {}), ) @@ -177,6 +178,11 @@ module "baseline" { lookup(local.baseline_environment_specific, "s3_buckets", {}), ) + schedule_alarms_lambda = merge( + lookup(local.baseline_all_environments, "schedule_alarms_lambda", {}), + lookup(local.baseline_environment_specific, "schedule_alarms_lambda", {}), + ) + secretsmanager_secrets = merge( module.baseline_presets.secretsmanager_secrets, lookup(local.baseline_all_environments, "secretsmanager_secrets", {}), diff --git a/terraform/environments/observability-platform/environment-configurations.tf b/terraform/environments/observability-platform/environment-configurations.tf index a50b07efc2e..4266af984e0 100644 --- a/terraform/environments/observability-platform/environment-configurations.tf +++ b/terraform/environments/observability-platform/environment-configurations.tf @@ -62,6 +62,7 @@ locals { "aws_accounts" = { "digital-prison-reporting-development" = { cloudwatch_enabled = true + cloudwatch_custom_namespaces = "DPRAgentCustomMetrics,DPRDataReconciliationCustom" prometheus_push_enabled = false amazon_prometheus_query_enabled = false xray_enabled = false @@ -69,6 +70,7 @@ locals { }, "digital-prison-reporting-preproduction" = { cloudwatch_enabled = true + cloudwatch_custom_namespaces = "DPRAgentCustomMetrics,DPRDataReconciliationCustom" prometheus_push_enabled = false amazon_prometheus_query_enabled = false xray_enabled = false @@ -76,6 +78,7 @@ locals { }, "digital-prison-reporting-test" = { cloudwatch_enabled = true + cloudwatch_custom_namespaces = "DPRAgentCustomMetrics,DPRDataReconciliationCustom" prometheus_push_enabled = false amazon_prometheus_query_enabled = false xray_enabled = false @@ -124,6 +127,13 @@ locals { "analytical-platform" = { identity_centre_team = "analytical-platform" aws_accounts = { + "analytical-platform-ingestion-development" = { + cloudwatch_enabled = true + prometheus_push_enabled = false + amazon_prometheus_query_enabled = false + xray_enabled = true + athena_enabled = false + }, "analytical-platform-ingestion-production" = { cloudwatch_enabled = true prometheus_push_enabled = false @@ -131,6 +141,14 @@ locals { xray_enabled = true athena_enabled = false }, + "analytical-platform-compute-development" = { + cloudwatch_enabled = true + prometheus_push_enabled = false + amazon_prometheus_query_enabled = true + amazon_prometheus_workspace_id = "ws-bfdd5d7a-5571-4686-bfd4-43ab07cf8d54ba" + xray_enabled = true + athena_enabled = false + }, "analytical-platform-compute-production" = { cloudwatch_enabled = true prometheus_push_enabled = false @@ -139,6 +157,14 @@ locals { xray_enabled = true athena_enabled = false }, + "analytical-platform-compute-test" = { + cloudwatch_enabled = true + prometheus_push_enabled = false + amazon_prometheus_query_enabled = true + amazon_prometheus_workspace_id = "ws-a9d7f576-58b7-4748-b4c1-b02bbdc54a2922" + xray_enabled = true + athena_enabled = false + }, "analytical-platform-production" = { cloudwatch_enabled = true prometheus_push_enabled = false @@ -149,10 +175,66 @@ locals { } } }, + "data-engineering" = { + "identity_centre_team" = "data-engineering", + "aws_accounts" = { + "analytical-platform-data-engineering-sandboxa" = { + cloudwatch_enabled = true + prometheus_push_enabled = false + amazon_prometheus_query_enabled = false + xray_enabled = false + athena_enabled = false + } + } + }, "digital-prison-reporting" = { "identity_centre_team" = "hmpps-digital-prison-reporting", "aws_accounts" = { + "digital-prison-reporting-development" = { + cloudwatch_enabled = true + cloudwatch_custom_namespaces = "DPRAgentCustomMetrics,DPRDataReconciliationCustom" + prometheus_push_enabled = false + amazon_prometheus_query_enabled = false + xray_enabled = false + athena_enabled = false + }, + "digital-prison-reporting-preproduction" = { + cloudwatch_enabled = true + cloudwatch_custom_namespaces = "DPRAgentCustomMetrics,DPRDataReconciliationCustom" + prometheus_push_enabled = false + amazon_prometheus_query_enabled = false + xray_enabled = false + athena_enabled = false + }, "digital-prison-reporting-production" = { + cloudwatch_enabled = true + cloudwatch_custom_namespaces = "DPRAgentCustomMetrics,DPRDataReconciliationCustom" + prometheus_push_enabled = false + amazon_prometheus_query_enabled = false + xray_enabled = false + athena_enabled = false + }, + "digital-prison-reporting-test" = { + cloudwatch_enabled = true + cloudwatch_custom_namespaces = "DPRAgentCustomMetrics,DPRDataReconciliationCustom" + prometheus_push_enabled = false + amazon_prometheus_query_enabled = false + xray_enabled = false + athena_enabled = false + } + } + }, + "digital-studio-operations" = { + "identity_centre_team" = "studio-webops" + "aws_accounts" = { + "nomis-test" = { + cloudwatch_enabled = true + prometheus_push_enabled = false + amazon_prometheus_query_enabled = false + xray_enabled = false + athena_enabled = false + } + "oasys-test" = { cloudwatch_enabled = true prometheus_push_enabled = false amazon_prometheus_query_enabled = false diff --git a/terraform/environments/panda-cyber-appsec-lab/ec2.tf b/terraform/environments/panda-cyber-appsec-lab/ec2.tf index e0ef2484906..12f11452239 100644 --- a/terraform/environments/panda-cyber-appsec-lab/ec2.tf +++ b/terraform/environments/panda-cyber-appsec-lab/ec2.tf @@ -1,12 +1,13 @@ # Kali Linux Instance resource "aws_instance" "kali_linux" { ami = "ami-0f398bcc12f72f967" // aws-marketplace/kali-last-snapshot-amd64-2024.2.0-804fcc46-63fc-4eb6-85a1-50e66d6c7215 - associate_public_ip_address = false + associate_public_ip_address = true instance_type = "t2.micro" subnet_id = module.vpc.private_subnets.0 vpc_security_group_ids = [aws_security_group.kali_linux_sg.id] iam_instance_profile = aws_iam_instance_profile.ssm_instance_profile.name ebs_optimized = true + metadata_options { http_tokens = "required" } @@ -53,12 +54,13 @@ resource "aws_instance" "kali_linux" { # Defect Dojo Instance resource "aws_instance" "defect_dojo" { ami = "ami-0e8d228ad90af673b" - associate_public_ip_address = false + associate_public_ip_address = true instance_type = "t2.micro" subnet_id = module.vpc.private_subnets.0 vpc_security_group_ids = [aws_security_group.kali_linux_sg.id] iam_instance_profile = aws_iam_instance_profile.ssm_instance_profile.name ebs_optimized = true + metadata_options { http_tokens = "required" } diff --git a/terraform/environments/planetfm/locals.tf b/terraform/environments/planetfm/locals.tf index f0e16d6eda3..3fc3d4c065e 100644 --- a/terraform/environments/planetfm/locals.tf +++ b/terraform/environments/planetfm/locals.tf @@ -24,6 +24,7 @@ locals { "network_lb", "ec2", "ec2_windows", + "ssm_command", ] cloudwatch_metric_alarms_default_actions = ["pagerduty"] cloudwatch_metric_oam_links_ssm_parameters = ["hmpps-oem-${local.environment}"] @@ -39,6 +40,7 @@ locals { enable_image_builder = true enable_s3_bucket = true enable_s3_software_bucket = true + enable_ssm_command_monitoring = true s3_iam_policies = ["EC2S3BucketWriteAndDeleteAccessPolicy"] } } diff --git a/terraform/environments/planetfm/main.tf b/terraform/environments/planetfm/main.tf index a23f7e6d41b..d234a1e3a07 100644 --- a/terraform/environments/planetfm/main.tf +++ b/terraform/environments/planetfm/main.tf @@ -74,6 +74,7 @@ module "baseline" { ) cloudwatch_metric_alarms = merge( + module.baseline_presets.cloudwatch_metric_alarms_baseline, lookup(local.baseline_all_environments, "cloudwatch_metric_alarms", {}), lookup(local.baseline_environment_specific, "cloudwatch_metric_alarms", {}), ) @@ -177,6 +178,11 @@ module "baseline" { lookup(local.baseline_environment_specific, "s3_buckets", {}), ) + schedule_alarms_lambda = merge( + lookup(local.baseline_all_environments, "schedule_alarms_lambda", {}), + lookup(local.baseline_environment_specific, "schedule_alarms_lambda", {}), + ) + secretsmanager_secrets = merge( module.baseline_presets.secretsmanager_secrets, lookup(local.baseline_all_environments, "secretsmanager_secrets", {}), diff --git a/terraform/environments/ppud/iam.tf b/terraform/environments/ppud/iam.tf index aa7104e6601..cd6af3eac6f 100644 --- a/terraform/environments/ppud/iam.tf +++ b/terraform/environments/ppud/iam.tf @@ -1172,15 +1172,34 @@ resource "aws_iam_policy" "iam_policy_for_lambda_cloudwatch_get_metric_data_dev" policy = jsonencode({ "Version" : "2012-10-17", "Statement" : [{ + "Sid" : "CloudwatchMetricPolicy", "Effect" : "Allow", "Action" : [ - "cloudwatch:GetMetricData" + "cloudwatch:GetMetricData", + "cloudwatch:ListMetrics" ], "Resource" : [ "arn:aws:ssm:eu-west-2:${local.environment_management.account_ids["ppud-development"]}:*" ] }, { + "Sid" : "SQSPolicy", + "Effect" : "Allow", + "Action" : [ + "sqs:ChangeMessageVisibility", + "sqs:DeleteMessage", + "sqs:GetQueueAttributes", + "sqs:GetQueueUrl", + "sqs:ListQueueTags", + "sqs:ReceiveMessage", + "sqs:SendMessage" + ], + "Resource" : [ + "arn:aws:sqs:eu-west-2:${local.environment_management.account_ids["ppud-production"]}:Lambda-Queue-Production" + ] + }, + { + "Sid" : "SESPolicy", "Effect" : "Allow", "Action" : [ "ses:SendEmail" diff --git a/terraform/environments/ppud/lambda.tf b/terraform/environments/ppud/lambda.tf index 302ec946fa5..9f93dcc960d 100644 --- a/terraform/environments/ppud/lambda.tf +++ b/terraform/environments/ppud/lambda.tf @@ -236,11 +236,11 @@ resource "aws_lambda_function" "terraform_lambda_enable_cpu_alarm" { resource "aws_lambda_permission" "allow_cloudwatch_to_call_lambda_terminate_cpu_process_dev" { count = local.is-development == true ? 1 : 0 - statement_id = "AllowExecutionFromCloudWatch" + statement_id = "AllowCloudWatchAccess" action = "lambda:InvokeFunction" function_name = aws_lambda_function.terraform_lambda_func_terminate_cpu_process_dev[0].function_name - principal = "lambda.alarms.cloudwatch.amazonaws.com" - source_arn = "arn:aws:cloudwatch:eu-west-2:${local.environment_management.account_ids["ppud-development"]}:alarm:*" + principal = "cloudwatch.amazonaws.com" + source_arn = "arn:aws:cloudwatch:eu-west-2:${local.environment_management.account_ids["ppud-development"]}:*" } resource "aws_lambda_function" "terraform_lambda_func_terminate_cpu_process_dev" { @@ -488,11 +488,11 @@ data "archive_file" "zip_the_send_cpu_notification_code_prod" { resource "aws_lambda_permission" "allow_cloudwatch_to_call_lambda_send_cpu_graph_dev" { count = local.is-development == true ? 1 : 0 - statement_id = "AllowExecutionFromCloudWatch" + statement_id = "AllowAccesstoCloudWatch" action = "lambda:InvokeFunction" function_name = aws_lambda_function.terraform_lambda_func_send_cpu_graph_dev[0].function_name - principal = "lambda.alarms.cloudwatch.amazonaws.com" - source_arn = "arn:aws:cloudwatch:eu-west-2:${local.environment_management.account_ids["ppud-development"]}:alarm:*" + principal = "cloudwatch.amazonaws.com" + source_arn = "arn:aws:cloudwatch:eu-west-2:${local.environment_management.account_ids["ppud-development"]}:*" } resource "aws_lambda_function" "terraform_lambda_func_send_cpu_graph_dev" { @@ -504,12 +504,12 @@ resource "aws_lambda_function" "terraform_lambda_func_send_cpu_graph_dev" { handler = "send_cpu_graph_dev.lambda_handler" runtime = "python3.12" timeout = 300 - depends_on = [aws_iam_role_policy_attachment.attach_lambda_policy_cloudwatch_invoke_lambda_to_lambda_role_cloudwatch_invoke_lambda_dev] + depends_on = [aws_iam_role_policy_attachment.attach_lambda_policy_cloudwatch_get_metric_data_to_lambda_role_cloudwatch_get_metric_data_dev] reserved_concurrent_executions = 5 # code_signing_config_arn = "arn:aws:lambda:eu-west-2:${local.environment_management.account_ids["ppud-development"]}:code-signing-config:csc-0c7136ccff2de748f" - dead_letter_config { - target_arn = aws_sqs_queue.lambda_queue_dev[0].arn - } + # dead_letter_config { + # target_arn = aws_sqs_queue.lambda_queue_dev[0].arn + # } tracing_config { mode = "Active" } diff --git a/terraform/environments/tribunals/cloudfront.tf b/terraform/environments/tribunals/cloudfront.tf index 489a37f5dc1..c81f76104f9 100644 --- a/terraform/environments/tribunals/cloudfront.tf +++ b/terraform/environments/tribunals/cloudfront.tf @@ -10,12 +10,12 @@ resource "aws_cloudfront_distribution" "tribunals_distribution" { origin_id = "tribunalsOrigin" custom_origin_config { - http_port = 80 - https_port = 443 - origin_protocol_policy = "https-only" - origin_ssl_protocols = ["TLSv1.2"] + http_port = 80 + https_port = 443 + origin_protocol_policy = "https-only" + origin_ssl_protocols = ["TLSv1.2"] origin_keepalive_timeout = 60 - origin_read_timeout = 60 + origin_read_timeout = 60 } custom_header { @@ -27,7 +27,7 @@ resource "aws_cloudfront_distribution" "tribunals_distribution" { default_cache_behavior { target_origin_id = "tribunalsOrigin" - cache_policy_id = data.aws_cloudfront_cache_policy.caching_disabled.id + cache_policy_id = data.aws_cloudfront_cache_policy.caching_disabled.id origin_request_policy_id = data.aws_cloudfront_origin_request_policy.all_viewer.id viewer_protocol_policy = "redirect-to-https" diff --git a/terraform/modules/baseline/ssm.tf b/terraform/modules/baseline/ssm.tf index 588f02e66a2..ffecf9b6634 100644 --- a/terraform/modules/baseline/ssm.tf +++ b/terraform/modules/baseline/ssm.tf @@ -59,7 +59,7 @@ resource "aws_ssm_association" "this" { apply_only_at_cron_interval = each.value.apply_only_at_cron_interval association_name = each.key - name = each.value.name + name = try(aws_ssm_document.this[each.value.name].name, each.value.name) # so ssm_doc is created first max_concurrency = each.value.max_concurrency max_errors = each.value.max_errors schedule_expression = each.value.schedule_expression diff --git a/terraform/modules/baseline_presets/cloudwatch_metric_alarms.tf b/terraform/modules/baseline_presets/cloudwatch_metric_alarms.tf index 2e16b5271e8..8a29488c93b 100644 --- a/terraform/modules/baseline_presets/cloudwatch_metric_alarms.tf +++ b/terraform/modules/baseline_presets/cloudwatch_metric_alarms.tf @@ -449,4 +449,25 @@ locals { } } } + + cloudwatch_metric_alarms_by_sns_topic = { + for sns_key, sns_value in local.sns_topics : sns_key => { + for namespace_key, namespace_value in local.cloudwatch_metric_alarms : namespace_key => { + for alarm_key, alarm_value in namespace_value : alarm_key => merge(alarm_value, { + alarm_actions = [sns_key] + ok_actions = [sns_key] + }) + } + } + } + + # alarms added via baseline. Put SSM command alerts in dso-pipelines so it doesn't clutter main application alerts + cloudwatch_metric_alarms_baseline = merge( + var.options.enable_ssm_command_monitoring ? { + "failed-ssm-command-${var.environment.account_name}" = local.cloudwatch_metric_alarms_by_sns_topic["dso-pipelines-pagerduty"].ssm.failed-ssm-command + } : {}, + var.options.enable_ssm_missing_metric_monitoring ? { + "ssm-command-metrics-missing-${var.environment.account_name}" = local.cloudwatch_metric_alarms_by_sns_topic["dso-pipelines-pagerduty"].ssm.ssm-command-metrics-missing + } : {}, + ) } diff --git a/terraform/modules/baseline_presets/outputs.tf b/terraform/modules/baseline_presets/outputs.tf index d085c54f7c9..01cc5560724 100644 --- a/terraform/modules/baseline_presets/outputs.tf +++ b/terraform/modules/baseline_presets/outputs.tf @@ -44,19 +44,15 @@ output "cloudwatch_metric_alarms" { value = local.cloudwatch_metric_alarms } +output "cloudwatch_metric_alarms_baseline" { + description = "Map of common cloudwatch metric alarms that can be passed into baseline directly as specified by var.options.enable_ssm_command_monitoring for example" + value = local.cloudwatch_metric_alarms_baseline +} + output "cloudwatch_metric_alarms_by_sns_topic" { description = "Map of sns topic key to cloudwatch metric alarms grouped by namespace, where the default action is the sns topic key" - value = { - for sns_key, sns_value in local.sns_topics : sns_key => { - for namespace_key, namespace_value in local.cloudwatch_metric_alarms : namespace_key => { - for alarm_key, alarm_value in namespace_value : alarm_key => merge(alarm_value, { - alarm_actions = [sns_key] - ok_actions = [sns_key] - }) - } - } - } + value = local.cloudwatch_metric_alarms_by_sns_topic } output "iam_roles" { diff --git a/terraform/modules/baseline_presets/sns_topics.tf b/terraform/modules/baseline_presets/sns_topics.tf index 89b75eade1a..346a1c9e403 100644 --- a/terraform/modules/baseline_presets/sns_topics.tf +++ b/terraform/modules/baseline_presets/sns_topics.tf @@ -6,8 +6,14 @@ # from the modernisation platform managed pagerduty_integration_keys locals { + + pagerduty_integrations = merge( + var.options.enable_ssm_command_monitoring ? { dso-pipelines-pagerduty = "dso-pipelines" } : {}, + var.options.sns_topics.pagerduty_integrations + ) + sns_topics_pagerduty_integrations = { - for key, value in var.options.sns_topics.pagerduty_integrations : key => { + for key, value in local.pagerduty_integrations : key => { display_name = "Pager duty integration for ${value}" kms_master_key_id = "general" subscriptions = { diff --git a/terraform/modules/baseline_presets/variables.tf b/terraform/modules/baseline_presets/variables.tf index 097391f56d0..e1a08a9bc19 100644 --- a/terraform/modules/baseline_presets/variables.tf +++ b/terraform/modules/baseline_presets/variables.tf @@ -42,6 +42,8 @@ variable "options" { enable_s3_db_backup_bucket = optional(bool, false) # create db-backup S3 buckets enable_s3_shared_bucket = optional(bool, false) # create devtest and preprodprod S3 bucket for sharing between accounts enable_s3_software_bucket = optional(bool, false) # create software S3 bucket in test account for image builder/configuration-management + enable_ssm_command_monitoring = optional(bool, false) # create SNS topic and alarms for SSM command monitoring + enable_ssm_missing_metric_monitoring = optional(bool, false) # create alarm if SSM command metrics are missing enable_vmimport = optional(bool, false) # create role for vm imports route53_resolver_rules = optional(map(list(string)), {}) # create route53 resolver rules; list of map keys to filter local.route53_resolver_rules_all iam_service_linked_roles = optional(list(string)) # create iam service linked roles; list of map keys to filter local.iam_service_linked_roles; default is to create all