diff --git a/.gitignore b/.gitignore index def069f7547..fe49cb52038 100644 --- a/.gitignore +++ b/.gitignore @@ -22,4 +22,6 @@ out/ .tfsec/ # vim -**/*.swp \ No newline at end of file +**/*.swp + +terraform/modules/**/*.zip diff --git a/terraform/environments/analytical-platform-compute/iam-policies.tf b/terraform/environments/analytical-platform-compute/iam-policies.tf index 18b8314df6e..f123f30634e 100644 --- a/terraform/environments/analytical-platform-compute/iam-policies.tf +++ b/terraform/environments/analytical-platform-compute/iam-policies.tf @@ -280,3 +280,22 @@ module "analytical_platform_lake_formation_share_policy" { policy = data.aws_iam_policy_document.analytical_platform_share_policy.json } + +data "aws_iam_policy_document" "s3_server_access_logs_policy" { + #checkov:skip=CKV_AWS_356:resource "*" limited by condition + statement { + sid = "S3ServerAccessLogsPolicy" + effect = "Allow" + actions = ["s3:PutObject"] + resources = ["*"] + principals { + type = "Service" + identifiers = ["logging.s3.amazonaws.com"] + } + condition { + test = "StringEquals" + variable = "aws:SourceAccount" + values = [data.aws_caller_identity.current.account_id] + } + } +} diff --git a/terraform/environments/analytical-platform-compute/kms-keys.tf b/terraform/environments/analytical-platform-compute/kms-keys.tf index 124bcb16bdd..e6b1a944f09 100644 --- a/terraform/environments/analytical-platform-compute/kms-keys.tf +++ b/terraform/environments/analytical-platform-compute/kms-keys.tf @@ -275,6 +275,66 @@ module "mlflow_s3_kms" { tags = local.tags } +module "mojap_derived_tables_replication_s3_kms" { + #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-derived-tables-replication"] + description = "mojap-derived-tables-replication S3 KMS key" + enable_default_policy = true + + deletion_window_in_days = 7 + + tags = local.tags +} + +module "mojap_compute_logs_s3_kms" { + #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-logs"] + description = "mojap-compute-logs S3 KMS key" + enable_default_policy = true + + deletion_window_in_days = 7 + + tags = local.tags + + key_statements = [ + { + sid = "AllowS3Logging" + effect = "Allow" + actions = [ + "kms:Encrypt", + "kms:Decrypt", + "kms:GenerateDataKey", + "kms:GenerateDataKeyWithoutPlaintext", + "kms:DescribeKey" + ] + resources = ["*"] + principals = [ + { + type = "Service" + identifiers = ["logging.s3.amazonaws.com"] + } + ] + conditions = [ + { + test = "StringEquals" + variable = "kms:ViaService" + values = ["logging.s3.amazonaws.com"] + } + ] + } + ] +} + module "common_secrets_manager_kms" { #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 73777c5ffce..621807a73cc 100644 --- a/terraform/environments/analytical-platform-compute/s3-buckets.tf +++ b/terraform/environments/analytical-platform-compute/s3-buckets.tf @@ -21,3 +21,70 @@ module "mlflow_bucket" { tags = local.tags } + +module "mojap_derived_tables_replication_bucket" { + #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.1" + + bucket = "mojap-compute-${local.environment}-derived-tables-replication" + + force_destroy = true + + 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_derived_tables_replication_s3_kms.key_arn + sse_algorithm = "aws:kms" + } + } + } + + logging = { + target_bucket = module.mojap_compute_logs_bucket.s3_bucket_id + target_prefix = "mojap-derived-tables-replication/" + } + + tags = local.tags +} + +module "mojap_compute_logs_bucket" { + #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.1" + + bucket = "mojap-compute-${local.environment}-logs" + + force_destroy = false + + policy = data.aws_iam_policy_document.s3_server_access_logs_policy.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_logs_s3_kms.key_arn + sse_algorithm = "aws:kms" + } + } + } + + tags = local.tags +} diff --git a/terraform/environments/contract-work-administration/app_servers.tf b/terraform/environments/contract-work-administration/app_servers.tf index bd44960778f..79649677fc8 100644 --- a/terraform/environments/contract-work-administration/app_servers.tf +++ b/terraform/environments/contract-work-administration/app_servers.tf @@ -62,7 +62,7 @@ do done echo "Updating /etc/rc.local file" -cat < etc/rc.local +cat < /etc/rc.local #!/bin/sh # # This script will be executed *after* all the other init scripts. @@ -116,8 +116,8 @@ sed -i 's/${local.application_data.accounts[local.environment].old_domain_name}/ ## Remove SSH key allowed echo "Removing old SSH key" -sed -i '/development-general$/d' /home/ec2-user/.ssh/authorized_keys -sed -i '/development-general$/d' /root/.ssh/authorized_keys +sed -i '/.*-general$/d' /home/ec2-user/.ssh/authorized_keys +sed -i '/.*-general$/d' /root/.ssh/authorized_keys sed -i '/testimage$/d' /root/.ssh/authorized_keys ## Add custom metric script @@ -181,7 +181,7 @@ resource "aws_instance" "app1" { iam_instance_profile = aws_iam_instance_profile.cwa.id key_name = aws_key_pair.cwa.key_name user_data_base64 = base64encode(local.app_userdata) - user_data_replace_on_change = true + user_data_replace_on_change = false metadata_options { http_tokens = "optional" } diff --git a/terraform/environments/contract-work-administration/application_variables.json b/terraform/environments/contract-work-administration/application_variables.json index 3c8905b97d8..4f63f97bb9e 100644 --- a/terraform/environments/contract-work-administration/application_variables.json +++ b/terraform/environments/contract-work-administration/application_variables.json @@ -43,9 +43,9 @@ "database_diskspace_threshold": "95", "database_read_write_ops_threshold": "1100000", "database_oradata_queue_length_threshold": "3", - "old_mail_server_url": "mail.aws.dev.legalservices.gov.uk", + "old_mail_server_url": "mail.aws.tst.legalservices.gov.uk", "laa_mail_relay_url": "laa-mail.laa-development.modernisation-platform.service.justice.gov.uk", - "old_domain_name": "dev.legalservices.gov.uk", + "old_domain_name": "tst.legalservices.gov.uk", "app_disk_space_alert_threshold": "92", "lz_account_id": "013163512034" }, diff --git a/terraform/environments/contract-work-administration/concurrent_manager.tf b/terraform/environments/contract-work-administration/concurrent_manager.tf index 01144e6a2d0..5caaf4ee525 100644 --- a/terraform/environments/contract-work-administration/concurrent_manager.tf +++ b/terraform/environments/contract-work-administration/concurrent_manager.tf @@ -63,7 +63,7 @@ do done echo "Updating /etc/rc.local file" -cat < etc/rc.local +cat < /etc/rc.local #!/bin/sh # # This script will be executed *after* all the other init scripts. @@ -116,8 +116,8 @@ sed -i 's/${local.application_data.accounts[local.environment].old_domain_name}/ ## Remove SSH key allowed echo "Removing old SSH key" -sed -i '/development-general$/d' /home/ec2-user/.ssh/authorized_keys -sed -i '/development-general$/d' /root/.ssh/authorized_keys +sed -i '/.*-general$/d' /home/ec2-user/.ssh/authorized_keys +sed -i '/.*-general$/d' /root/.ssh/authorized_keys sed -i '/testimage$/d' /root/.ssh/authorized_keys ## Add custom metric script @@ -158,7 +158,7 @@ resource "aws_instance" "concurrent_manager" { iam_instance_profile = aws_iam_instance_profile.cwa.id key_name = aws_key_pair.cwa.key_name user_data_base64 = base64encode(local.cm_userdata) - user_data_replace_on_change = true + user_data_replace_on_change = false metadata_options { http_tokens = "optional" } diff --git a/terraform/environments/contract-work-administration/database.tf b/terraform/environments/contract-work-administration/database.tf index 86b42d9c381..aec87ed93c0 100644 --- a/terraform/environments/contract-work-administration/database.tf +++ b/terraform/environments/contract-work-administration/database.tf @@ -99,6 +99,8 @@ echo "Adding disk space script" chmod 766 /home/oracle/scripts/disk_space.sh sed -i "s/SLACK_ALERT_URL/$SLACK_ALERT_URL/g" /home/oracle/scripts/disk_space.sh +sed -i "/^mail.*tablespace.warning$/c\mailx -s \"\$ORACLE_SID on \$\{hostname\}: ${upper(local.application_data.accounts[local.environment].env_short)} CWA Tablespace Warning\" $SLACK_ALERT_URL < /tmp/tablespace.warning" /home/oracle/scripts/tablespace1.sh + echo "Setting up AWS EBS backup" INSTANCE_ID=$(curl http://169.254.169.254/latest/meta-data/instance-id) cat < /home/oracle/scripts/aws_ebs_backup.sh @@ -113,7 +115,9 @@ chmod 744 /home/oracle/scripts/aws_ebs_backup.sh echo "Setting up cron jobs" su oracle -c "crontab -l > /home/oracle/oraclecrontab.txt" +sed -i '/disk_space.sh/d' /home/oracle/oraclecrontab.txt echo "00 02 * * * /home/oracle/scripts/aws_ebs_backup.sh > /tmp/aws_ebs_backup.log" >> /home/oracle/oraclecrontab.txt +echo "0,30 08-17 * * 1-5 /home/oracle/scripts/disk_space.sh ${upper(local.application_data.accounts[local.environment].env_short)} ${local.application_data.accounts[local.environment].app_disk_space_alert_threshold} >/tmp/disk_space.trc 2>&1" >> /home/oracle/oraclecrontab.txt chown oracle:oinstall /home/oracle/oraclecrontab.txt chmod 744 /home/oracle/oraclecrontab.txt @@ -124,8 +128,8 @@ ln -s /bin/mail /bin/mailx ## Remove SSH key allowed echo "Removing old SSH key" -sed -i '/development-general$/d' /home/ec2-user/.ssh/authorized_keys -sed -i '/development-general$/d' /root/.ssh/authorized_keys +sed -i '/.*-general$/d' /home/ec2-user/.ssh/authorized_keys +sed -i '/.*-general$/d' /root/.ssh/authorized_keys sed -i '/testimage$/d' /root/.ssh/authorized_keys ## Add custom metric script diff --git a/terraform/environments/contract-work-administration/scripts/disk-space-alert.sh b/terraform/environments/contract-work-administration/scripts/disk-space-alert.sh index 2d9f3ecdbae..a0e23b4f5b9 100644 --- a/terraform/environments/contract-work-administration/scripts/disk-space-alert.sh +++ b/terraform/environments/contract-work-administration/scripts/disk-space-alert.sh @@ -1,7 +1,7 @@ #!/bin/bash if [ $# -ne 2 ]; then - echo "1st parameter is ENV, 2nd parameter is % usage. 3rd parameter is the Slack URL to alert to" + echo "1st parameter is ENV, 2nd parameter is % usage" else diff --git a/terraform/environments/corporate-information-system/ec2.tf b/terraform/environments/corporate-information-system/ec2.tf index 15b466a576a..6d9547aa1cf 100644 --- a/terraform/environments/corporate-information-system/ec2.tf +++ b/terraform/environments/corporate-information-system/ec2.tf @@ -27,7 +27,7 @@ resource "aws_instance" "cis_db_instance" { } metadata_options { - http_tokens = "optional" + http_tokens = "required" } tags = merge( diff --git a/terraform/environments/corporate-information-system/locals.tf b/terraform/environments/corporate-information-system/locals.tf index 389df111a59..731a1f29488 100644 --- a/terraform/environments/corporate-information-system/locals.tf +++ b/terraform/environments/corporate-information-system/locals.tf @@ -14,6 +14,16 @@ sed -i 's/#ClientAliveInterval.*/ClientAliveInterval 1200/' /etc/ssh/sshd_config sed -i 's/#ClientAliveCountMax.*/ClientAliveCountMax 3/' /etc/ssh/sshd_config service sshd restart +# Add TCP keepalive time to sysctl.conf ---> keepalive solution +echo "net.ipv4.tcp_keepalive_time = 300" >> /etc/sysctl.conf +sysctl -p + +# Add SQLNET.EXPIRE_TIME to sqlnet.ora ---> keepalive solution +echo "SQLNET.EXPIRE_TIME = 5" >> /oracle/software/product/10.2.0/network/admin/sqlnet.ora + +# Modify tnsnames.ora to insert (ENABLE=broken) ---> keepalive solution +sed -i '/(DESCRIPTION =/a\\ (ENABLE=broken)' /oracle/software/product/10.2.0/network/admin/tnsnames.ora + # Changes to oracle files sed -i 's|cis.*legalservices.gov.uk:8080|${local.application_name_short}.${data.aws_route53_zone.external.name}:8080|' /home/batman/bin/dkj-shell-funcs sed -i 's|cis.*legalservices.gov.uk|${local.application_name_short}.${data.aws_route53_zone.external.name}|' /oracle/software/product/10.2.0/network/admin/listener.ora diff --git a/terraform/environments/delius-core/fargate_graceful_retirement.tf b/terraform/environments/delius-core/fargate_graceful_retirement.tf new file mode 100644 index 00000000000..41ab31c395f --- /dev/null +++ b/terraform/environments/delius-core/fargate_graceful_retirement.tf @@ -0,0 +1,7 @@ +module "fargate_graceful_retirement" { + count = local.environment == "development" ? 1 : 0 + source = "../../modules/fargate_graceful_retirement" + restart_time = "22:00" + restart_day_of_the_week = "WEDNESDAY" + debug_logging = true +} diff --git a/terraform/environments/delius-core/locals_development.tf b/terraform/environments/delius-core/locals_development.tf index 5ad2a0cb6be..10a8153751f 100644 --- a/terraform/environments/delius-core/locals_development.tf +++ b/terraform/environments/delius-core/locals_development.tf @@ -38,6 +38,7 @@ locals { instance_policies = { "business_unit_kms_key_access" = aws_iam_policy.business_unit_kms_key_access } + inline_ebs = false primary_instance_count = 1 standby_count = 2 ebs_volumes = { diff --git a/terraform/environments/delius-core/locals_preproduction.tf b/terraform/environments/delius-core/locals_preproduction.tf index 8d9b665ab47..1966ec6d8ce 100644 --- a/terraform/environments/delius-core/locals_preproduction.tf +++ b/terraform/environments/delius-core/locals_preproduction.tf @@ -37,6 +37,7 @@ locals { instance_policies = { "business_unit_kms_key_access" = aws_iam_policy.business_unit_kms_key_access } + inline_ebs = true primary_instance_count = 0 standby_count = 0 ebs_volumes = { diff --git a/terraform/environments/delius-core/locals_stage.tf b/terraform/environments/delius-core/locals_stage.tf index 2391ee007dd..c37f4d20748 100644 --- a/terraform/environments/delius-core/locals_stage.tf +++ b/terraform/environments/delius-core/locals_stage.tf @@ -32,8 +32,11 @@ locals { db_config_stage = { - instance_type = "r7i.2xlarge" - ami_name_regex = "^delius_core_ol_8_5_oracle_db_19c_patch_2024-06-04T11-24-58.162Z" + instance_type = "r7i.2xlarge" + ami_name_regex = "^delius_core_ol_8_5_oracle_db_19c_patch_2024-06-04T11-24-58.162Z" + + inline_ebs = true + primary_instance_count = 1 standby_count = 0 diff --git a/terraform/environments/delius-core/locals_test.tf b/terraform/environments/delius-core/locals_test.tf index ff6a4f689da..b032c40d6ce 100644 --- a/terraform/environments/delius-core/locals_test.tf +++ b/terraform/environments/delius-core/locals_test.tf @@ -38,6 +38,9 @@ locals { instance_policies = { "business_unit_kms_key_access" = aws_iam_policy.business_unit_kms_key_access } + + inline_ebs = true + primary_instance_count = 1 standby_count = 0 ebs_volumes = { 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 76dce3f9f8b..9ef35655bdb 100644 --- a/terraform/environments/delius-core/modules/components/dms/cloudwatch-alarms.tf +++ b/terraform/environments/delius-core/modules/components/dms/cloudwatch-alarms.tf @@ -70,8 +70,8 @@ resource "aws_cloudwatch_metric_alarm" "dms_cdc_latency_source" { statistic = "Average" metric_name = "CDCLatencySource" comparison_operator = "GreaterThanThreshold" - threshold = 10 - evaluation_periods = 2 + threshold = 15 + evaluation_periods = 3 period = 30 actions_enabled = true alarm_actions = [aws_sns_topic.dms_alerting.arn] @@ -92,8 +92,8 @@ resource "aws_cloudwatch_metric_alarm" "dms_cdc_latency_target" { statistic = "Average" metric_name = "CDCLatencyTarget" comparison_operator = "GreaterThanThreshold" - threshold = 10 - evaluation_periods = 2 + threshold = 15 + evaluation_periods = 3 period = 30 actions_enabled = true alarm_actions = [aws_sns_topic.dms_alerting.arn] diff --git a/terraform/environments/delius-core/modules/components/oracle_db_instance/instance.tf b/terraform/environments/delius-core/modules/components/oracle_db_instance/instance.tf index 79047bfa77d..0cfda5ae333 100644 --- a/terraform/environments/delius-core/modules/components/oracle_db_instance/instance.tf +++ b/terraform/environments/delius-core/modules/components/oracle_db_instance/instance.tf @@ -8,7 +8,7 @@ locals { metadata_endpoint_enabled = var.metadata_options.http_endpoint metadata_options_http_tokens = var.metadata_options.http_tokens monitoring = var.monitoring - ebs_block_device_inline = true + ebs_block_device_inline = false vpc_security_group_ids = var.security_group_ids private_dns_name_options = { enable_resource_name_dns_aaaa_record = false diff --git a/terraform/environments/delius-core/modules/components/oracle_db_instance/variables.tf b/terraform/environments/delius-core/modules/components/oracle_db_instance/variables.tf index 038e936f731..846034d9346 100644 --- a/terraform/environments/delius-core/modules/components/oracle_db_instance/variables.tf +++ b/terraform/environments/delius-core/modules/components/oracle_db_instance/variables.tf @@ -176,3 +176,9 @@ variable "sns_topic_arn" { description = "The ARN of the SNS topic" type = string } + +variable "inline_ebs" { + default = true + type = bool + description = "Whether to create EBS volumes inline with the instance" +} diff --git a/terraform/environments/delius-core/modules/delius_environment/database.tf b/terraform/environments/delius-core/modules/delius_environment/database.tf index c2d83ed639e..84d7a8cab9f 100644 --- a/terraform/environments/delius-core/modules/delius_environment/database.tf +++ b/terraform/environments/delius-core/modules/delius_environment/database.tf @@ -49,6 +49,7 @@ module "oracle_db_primary" { ebs_volumes = var.db_config.ebs_volumes ebs_volume_config = var.db_config.ebs_volume_config + inline_ebs = var.db_config.inline_ebs env_name = var.env_name environment_config = var.environment_config @@ -101,6 +102,8 @@ module "oracle_db_standby" { ebs_volumes = var.db_config.ebs_volumes ebs_volume_config = var.db_config.ebs_volume_config + inline_ebs = var.db_config.inline_ebs + env_name = var.env_name environment_config = var.environment_config subnet_id = var.account_config.ordered_private_subnet_ids[(count.index + length(module.oracle_db_primary)) % 3] diff --git a/terraform/environments/edw/bastion_linux.json b/terraform/environments/edw/bastion_linux.json index 4c1fd6e1a2e..7cb732b7ac6 100644 --- a/terraform/environments/edw/bastion_linux.json +++ b/terraform/environments/edw/bastion_linux.json @@ -6,7 +6,7 @@ "saanchi": "ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIMAY8Vb7XTq4gpyAO1s7HsNpwlteXkuTTa+UpRQtaHW1 saanchi.dubey@L0517", "wendy": "ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABgQDENKJfNawEbz2pdXwXPUu1tKgJJmOJ0v6vjy5owJ43O9ghBWHA/v9Dpx/x2ONUS4uFQoANKWSI3pl07oCVodu/izn7cnM4CPmHfgNETpHQoJsEi11bUQbd43AAr4Lt2cv7re8TM4Q0scjIryXXhal8WGHbKw66g9a9OYzU/Zou0EChxZ+N3vYj0WPIB9mPuTh5UDn+rgyOho/yd68wZmy/oiuujZ3rWCf/Uyy1j0bV/zPqZ+Fxk7pSoFZ+7tOf8q6el3zLUrGnu+c9VOuNTpgRjX+tBjfBUNyP92apRdjRkjfXiO7cN/dfAVCAqBGV4EG2Doy0JCP2HqmnuJWJh+lW6eIOfRknFf6MCbscoaMKepKjTWgR+LXqCkGhfchKHsgpkS3BmIcIQmDmRHMDF+MDlZ6IG17ptMbJ80Lggdu91mTVz9eVUOI1zeIDUSWnHkBSg7rod/FrFjDC5VZ8H5YEpFMC0a4f0gXnTRkuRduuH8Iw44NjpXewSWYXasDPpDM= wendycalverly@L0520", "mohamed": "ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABgQDIkP6+D3iZMD5RpvuHrDfZV5Ke4HLRrD12xyrbRGpSGgOzrgJS4RZOe2gU0DTYtBymZGDN2koE0SDWwKSUrNDUPdWNvbgubaYJNYfvkqVRyRmTHx0pqrEHmecESM3b6Y8R1tcY2e8l4xEmy/I+8ADdOyxhi0b8iEZG8BTyDfjV/fXabtz/L7+Z9DnmwJc8b1dzg5aP2J3qlbBdqH1p2smJe8p6WFe5dVC6IP2TIAk96R0/tOPRD76kDx+Kj6dbcO/oPabGg7Fo5OZ95H3OGkzQOUH83BiWnYsSXrxwu5JR8qV+jMP4+dYO0O5J98vZjqMszyKqP0A5FA1rnEL75i4o7a/nalQ8brZzvstL72/CAPbxzhHPwG1yFnaiU9wfhyOqGpCNHSfOvYvSGr0U52QUVKdiAsy5eZTogN1TXhLoXQs1b30rRm6JjnkeQ1Jfj8LwIk1xGp2vjuxeZuE8fnTv5OsOcagslDTPMNW1LYnMecZp4bwPhbdhCi1JkEVYGrM= mohamed.nasr@MJ004347", - "steve": "ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABgQDCP8zh2MJzL9MY/H19YNk562OfF6PJI9/cxfihIldjxFN6CRKHX2MSAxFUt08jANIx2GV/KLYsgiKOu89EmFmjr1ySz0R7gydkh/zpiTSL1A6Pnb2AnMnXRo+BBS8h6y/3yXd6MUtS4jqeE2a0/VHCOX/pgPv3d/CQcJ4fCI3NjnM24YAPcGwpsMo71L09V9guJ6yao4uo6tdCcnXrDi10A4oLUml/Vcb5UfeDWMh81hVNnuZWq2rgY5V6fHdzuKfLmSVzoSeXxE6rHKjqx3JWSdAQMH4xW6+K5NiKGsUb2bNHzatLUkkazWGqo2uvBtDotbmqb6RXrAXS5y/uBJ+qZ29gKPA28zQoVkDlX208PW0SZsWovHo2lwTPwxTl8dMOzO5nxmwCe/b9RYTxFN07Sk7TC1hCdXAKgJueBkG1MRBs7Nwe+4NUkNHxfd6DjP3dJi0PwBdokL2iKlIoIIeVKFBBqmFJ52JeLA8bWqgYhk1h6Io7+QPyF0xrwXU+v4c= stephen.linden@MJ003791" + "steve": "ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABgQDBoXZLWX9TSSvq3rFWANz0YHpPHqkMrhrxeIpAXOhMWQR0wE75a8rxOhfpClR2S1eRPnuwQ9FvMIZNp72DyawHF+ToGYV/iIhVNIqpGktVZCEOiizjhGRmWgX+0z1/VSFGbl1ocAoZxeUOrnbxtiy4u/zGjxgAGrmhbVDWcS9v9tBHHCGcvnpDryPT533aSzdgeI6fzAQLIu9lktMQE7DMfqtj+S4EUuLMYU3IIaqSUt2yKrTCYbCgb+RSVx0BrL3pg9VToneQDpExCsRh5QRyMheQ6ukAQ4NneV0gre/iS2MVopZwroQ408a4MF6noixgUeddOw+2okFj++S68hITiq99x2AyxppdkIQ85uiyVjobqsd/AH0QXaDnRwccefKkKL/ieHIpW6OlrpY77Wg/p8QJz6GsymcaQavIDy8OOgV8kltuRvpAv9CARVLJXgQNp9goJH5fN8unnPgXVUdtk4rP5N8fHRGV1j6/pf+U4Sw/QpnUXhsHLpVM0cD5vbk= stephen.linden@MJ003791" }, "preproduction": {}, "production": {} diff --git a/terraform/environments/example/certificates.tf b/terraform/environments/example/certificates.tf index 31864376d43..b5c33dc9414 100644 --- a/terraform/environments/example/certificates.tf +++ b/terraform/environments/example/certificates.tf @@ -60,32 +60,32 @@ # # Build loadbalancer #tfsec:ignore:aws-elb-alb-not-public as the external lb needs to be public. -resource "aws_lb" "certificate_example_lb" { - #checkov:skip=CKV2_AWS_28:Ensure public facing ALB are protected by WAF - name = "certificate-example-loadbalancer" - load_balancer_type = "application" - subnets = data.aws_subnets.shared-public.ids - #checkov:skip=CKV_AWS_150:Short-lived example environment, hence no need for deletion protection - enable_deletion_protection = false - # allow 60*4 seconds before 504 gateway timeout for long-running DB operations - idle_timeout = 240 - drop_invalid_header_fields = true +# resource "aws_lb" "certificate_example_lb" { +# #checkov:skip=CKV2_AWS_28:Ensure public facing ALB are protected by WAF +# name = "certificate-example-loadbalancer" +# load_balancer_type = "application" +# subnets = data.aws_subnets.shared-public.ids +# #checkov:skip=CKV_AWS_150:Short-lived example environment, hence no need for deletion protection +# enable_deletion_protection = false +# # allow 60*4 seconds before 504 gateway timeout for long-running DB operations +# idle_timeout = 240 +# drop_invalid_header_fields = true - security_groups = [aws_security_group.certificate_example_load_balancer_sg.id] +# security_groups = [aws_security_group.certificate_example_load_balancer_sg.id] - access_logs { - bucket = module.s3-bucket-lb.bucket.id - prefix = "test-lb" - enabled = true - } +# access_logs { +# bucket = module.s3-bucket-lb.bucket.id +# prefix = "test-lb" +# enabled = true +# } - tags = { Name = "${local.application_name}-external-loadbalancer" } - depends_on = [aws_security_group.certificate_example_load_balancer_sg] -} +# tags = { Name = "${local.application_name}-external-loadbalancer" } +# depends_on = [aws_security_group.certificate_example_load_balancer_sg] +# } -resource "aws_security_group" "certificate_example_load_balancer_sg" { - name = "certificate-example-lb-sg" - description = "controls access to load balancer" - vpc_id = data.aws_vpc.shared.id - tags = { Name = lower(format("lb-sg-%s-%s-example", local.application_name, local.environment)) } -} \ No newline at end of file +# resource "aws_security_group" "certificate_example_load_balancer_sg" { +# name = "certificate-example-lb-sg" +# description = "controls access to load balancer" +# vpc_id = data.aws_vpc.shared.id +# tags = { Name = lower(format("lb-sg-%s-%s-example", local.application_name, local.environment)) } +# } \ No newline at end of file diff --git a/terraform/environments/example/clean.sh b/terraform/environments/example/clean.sh deleted file mode 100755 index d1084783297..00000000000 --- a/terraform/environments/example/clean.sh +++ /dev/null @@ -1,4 +0,0 @@ -rm -Rf .terraform -rm .terraform.lock.hcl -terraform init -backend-config=assume_role={role_arn=\"arn:aws:iam::946070829339:role/modernisation-account-terraform-state-member-access\"} -terraform workspace select example-development \ No newline at end of file diff --git a/terraform/environments/example/data.tf b/terraform/environments/example/data.tf index fb01c43c501..67a57156522 100644 --- a/terraform/environments/example/data.tf +++ b/terraform/environments/example/data.tf @@ -1,3 +1,7 @@ +########################################################################################### +#------------------------Comment out file if not required---------------------------------- +########################################################################################### + #### This file can be used to store data specific to the member account #### #For macie code diff --git a/terraform/environments/example/ec2_bastion_linux.tf b/terraform/environments/example/ec2_bastion_linux.tf index aca36eb6dc2..f650ad4a7df 100644 --- a/terraform/environments/example/ec2_bastion_linux.tf +++ b/terraform/environments/example/ec2_bastion_linux.tf @@ -1,35 +1,38 @@ +########################################################################################### +#------------------------Comment out file if not required---------------------------------- +########################################################################################### # # tfsec:ignore:aws-s3-enable-bucket-encryption tfsec:ignore:aws-s3-encryption-customer-key tfsec:ignore:aws-s3-enable-bucket-logging tfsec:ignore:aws-s3-enable-versioning -module "bastion_linux" { - source = "github.com/ministryofjustice/modernisation-platform-terraform-bastion-linux?ref=95ed3c3f454e2014a62990aacd5d68c64d026f11" #v4.2.1 +# module "bastion_linux" { +# source = "github.com/ministryofjustice/modernisation-platform-terraform-bastion-linux?ref=95ed3c3f454e2014a62990aacd5d68c64d026f11" #v4.2.1 - providers = { - aws.share-host = aws.core-vpc # core-vpc-(environment) holds the networking for all accounts - aws.share-tenant = aws # The default provider (unaliased, `aws`) is the tenant - } - # s3 - used for logs and user ssh public keys - bucket_name = "bastion-example" - # public keys - public_key_data = local.public_key_data.keys[local.environment] - # logs - log_auto_clean = "Enabled" - log_standard_ia_days = 30 # days before moving to IA storage - log_glacier_days = 60 # days before moving to Glacier - log_expiry_days = 180 # days before log expiration - # bastion - allow_ssh_commands = false - app_name = var.networking[0].application - business_unit = local.vpc_name - subnet_set = local.subnet_set - environment = local.environment - region = "eu-west-2" +# providers = { +# aws.share-host = aws.core-vpc # core-vpc-(environment) holds the networking for all accounts +# aws.share-tenant = aws # The default provider (unaliased, `aws`) is the tenant +# } +# # s3 - used for logs and user ssh public keys +# bucket_name = "bastion-example" +# # public keys +# public_key_data = local.public_key_data.keys[local.environment] +# # logs +# log_auto_clean = "Enabled" +# log_standard_ia_days = 30 # days before moving to IA storage +# log_glacier_days = 60 # days before moving to Glacier +# log_expiry_days = 180 # days before log expiration +# # bastion +# allow_ssh_commands = false +# app_name = var.networking[0].application +# business_unit = local.vpc_name +# subnet_set = local.subnet_set +# environment = local.environment +# region = "eu-west-2" - # Tags - tags_common = local.tags - tags_prefix = terraform.workspace -} +# # Tags +# tags_common = local.tags +# tags_prefix = terraform.workspace +# } -locals { - public_key_data = jsondecode(file("./bastion_linux.json")) -} +# locals { +# public_key_data = jsondecode(file("./bastion_linux.json")) +# } diff --git a/terraform/environments/example/identity_store.tf b/terraform/environments/example/identity_store.tf index 3d4a2cea32c..41b683f4fb9 100644 --- a/terraform/environments/example/identity_store.tf +++ b/terraform/environments/example/identity_store.tf @@ -1,3 +1,7 @@ +########################################################################################### +#------------------------Comment out file if not required---------------------------------- +########################################################################################### + # data "aws_ssoadmin_instances" "example" { # provider = aws.sso-readonly # } diff --git a/terraform/environments/example/loadbalancer.tf b/terraform/environments/example/loadbalancer.tf index 3450069ae43..82c248b9f26 100644 --- a/terraform/environments/example/loadbalancer.tf +++ b/terraform/environments/example/loadbalancer.tf @@ -4,167 +4,167 @@ # Build loadbalancer security group -resource "aws_security_group" "example_load_balancer_sg" { - name = "example-lb-sg" - description = "controls access to load balancer" - vpc_id = data.aws_vpc.shared.id - tags = { Name = lower(format("lb-sg-%s-%s-example", local.application_name, local.environment)) } - - # Set up the ingress and egress parts of the security group -} -resource "aws_security_group_rule" "ingress_traffic_lb" { - for_each = local.application_data.example_ec2_sg_rules - description = format("Traffic for %s %d", each.value.protocol, each.value.from_port) - from_port = each.value.from_port - protocol = each.value.protocol - security_group_id = aws_security_group.example_load_balancer_sg.id - to_port = each.value.to_port - type = "ingress" - cidr_blocks = [data.aws_vpc.shared.cidr_block] -} -resource "aws_security_group_rule" "egress_traffic_lb" { - for_each = local.application_data.example_ec2_sg_rules - description = format("Outbound traffic for %s %d", each.value.protocol, each.value.from_port) - from_port = each.value.from_port - protocol = each.value.protocol - security_group_id = aws_security_group.example_load_balancer_sg.id - to_port = each.value.to_port - type = "egress" - source_security_group_id = aws_security_group.example_load_balancer_sg.id -} - -# # Build loadbalancer -#tfsec:ignore:aws-elb-alb-not-public as the external lb needs to be public. -resource "aws_lb" "external" { - name = "${local.application_name}-loadbalancer" - load_balancer_type = "application" - subnets = data.aws_subnets.shared-public.ids - #checkov:skip=CKV_AWS_150:Short-lived example environment, hence no need for deletion protection - enable_deletion_protection = false - # allow 60*4 seconds before 504 gateway timeout for long-running DB operations - idle_timeout = 240 - drop_invalid_header_fields = true - - security_groups = [aws_security_group.example_load_balancer_sg.id] - - access_logs { - bucket = module.s3-bucket-lb.bucket.id - prefix = "test-lb" - enabled = true - } - - tags = { Name = "${local.application_name}-external-loadbalancer" } - depends_on = [aws_security_group.example_load_balancer_sg] -} -# Create the target group -resource "aws_lb_target_group" "target_group" { - #checkov:skip=CKV_AWS_378: "Ensure AWS Load Balancer doesn't use HTTP protocol" - name = "${local.application_name}-tg-${local.environment}" - port = local.application_data.accounts[local.environment].server_port - protocol = "HTTP" - vpc_id = data.aws_vpc.shared.id - target_type = "instance" - deregistration_delay = 30 - - stickiness { - type = "lb_cookie" - } - #checkov:skip=CKV_AWS_261: "health_check defined below, but not picked up" - health_check { - healthy_threshold = "5" - interval = "120" - protocol = "HTTP" - unhealthy_threshold = "2" - matcher = "200-499" - timeout = "5" - } - - tags = { Name = "${local.application_name}-tg-${local.environment}" } - lifecycle { - create_before_destroy = true - } -} +# resource "aws_security_group" "example_load_balancer_sg" { +# name = "example-lb-sg" +# description = "controls access to load balancer" +# vpc_id = data.aws_vpc.shared.id +# tags = { Name = lower(format("lb-sg-%s-%s-example", local.application_name, local.environment)) } + +# # Set up the ingress and egress parts of the security group +# } +# resource "aws_security_group_rule" "ingress_traffic_lb" { +# for_each = local.application_data.example_ec2_sg_rules +# description = format("Traffic for %s %d", each.value.protocol, each.value.from_port) +# from_port = each.value.from_port +# protocol = each.value.protocol +# security_group_id = aws_security_group.example_load_balancer_sg.id +# to_port = each.value.to_port +# type = "ingress" +# cidr_blocks = [data.aws_vpc.shared.cidr_block] +# } +# resource "aws_security_group_rule" "egress_traffic_lb" { +# for_each = local.application_data.example_ec2_sg_rules +# description = format("Outbound traffic for %s %d", each.value.protocol, each.value.from_port) +# from_port = each.value.from_port +# protocol = each.value.protocol +# security_group_id = aws_security_group.example_load_balancer_sg.id +# to_port = each.value.to_port +# type = "egress" +# source_security_group_id = aws_security_group.example_load_balancer_sg.id +# } + +# # # Build loadbalancer +# #tfsec:ignore:aws-elb-alb-not-public as the external lb needs to be public. +# resource "aws_lb" "external" { +# name = "${local.application_name}-loadbalancer" +# load_balancer_type = "application" +# subnets = data.aws_subnets.shared-public.ids +# #checkov:skip=CKV_AWS_150:Short-lived example environment, hence no need for deletion protection +# enable_deletion_protection = false +# # allow 60*4 seconds before 504 gateway timeout for long-running DB operations +# idle_timeout = 240 +# drop_invalid_header_fields = true + +# security_groups = [aws_security_group.example_load_balancer_sg.id] + +# access_logs { +# bucket = module.s3-bucket-lb.bucket.id +# prefix = "test-lb" +# enabled = true +# } + +# tags = { Name = "${local.application_name}-external-loadbalancer" } +# depends_on = [aws_security_group.example_load_balancer_sg] +# } +# # # Create the target group +# resource "aws_lb_target_group" "target_group" { +# #checkov:skip=CKV_AWS_378: "Ensure AWS Load Balancer doesn't use HTTP protocol" +# name = "${local.application_name}-tg-${local.environment}" +# port = local.application_data.accounts[local.environment].server_port +# protocol = "HTTP" +# vpc_id = data.aws_vpc.shared.id +# target_type = "instance" +# deregistration_delay = 30 + +# stickiness { +# type = "lb_cookie" +# } +# #checkov:skip=CKV_AWS_261: "health_check defined below, but not picked up" +# health_check { +# healthy_threshold = "5" +# interval = "120" +# protocol = "HTTP" +# unhealthy_threshold = "2" +# matcher = "200-499" +# timeout = "5" +# } + +# tags = { Name = "${local.application_name}-tg-${local.environment}" } +# lifecycle { +# create_before_destroy = true +# } +# } # Link target group to the EC2 instance on port 80 -resource "aws_lb_target_group_attachment" "develop" { - target_group_arn = aws_lb_target_group.target_group.arn - target_id = aws_instance.lb_example_instance.id - port = 80 -} +# resource "aws_lb_target_group_attachment" "develop" { +# target_group_arn = aws_lb_target_group.target_group.arn +# target_id = aws_instance.lb_example_instance.id +# port = 80 +# } # Load balancer listener -resource "aws_lb_listener" "external" { - load_balancer_arn = aws_lb.external.arn - port = local.application_data.accounts[local.environment].server_port - protocol = local.application_data.accounts[local.environment].lb_listener_protocol - #checkov:skip=CKV_AWS_2: "protocol for lb set in application_variables" - ssl_policy = local.application_data.accounts[local.environment].lb_listener_protocol == "HTTP" ? "" : "ELBSecurityPolicy-TLS13-1-2-2021-06" - #checkov:skip=CKV_AWS_103: "ssl_policy for lb set in application_variables" - - default_action { - type = "forward" - target_group_arn = aws_lb_target_group.target_group.arn - } -} - -# # This will build on the core-vpc development account under platforms-development.modernisation-platform.service.justice.gov.uk, and route traffic back to example LB -resource "aws_route53_record" "example" { - provider = aws.core-vpc - zone_id = data.aws_route53_zone.external.zone_id - name = "${var.networking[0].application}.${var.networking[0].business-unit}-${local.environment}.modernisation-platform.service.justice.gov.uk" - type = "A" - - alias { - name = aws_lb.external.dns_name - zone_id = aws_lb.external.zone_id - evaluate_target_health = true - } -} +# resource "aws_lb_listener" "external" { +# load_balancer_arn = aws_lb.external.arn +# port = local.application_data.accounts[local.environment].server_port +# protocol = local.application_data.accounts[local.environment].lb_listener_protocol +# #checkov:skip=CKV_AWS_2: "protocol for lb set in application_variables" +# ssl_policy = local.application_data.accounts[local.environment].lb_listener_protocol == "HTTP" ? "" : "ELBSecurityPolicy-TLS13-1-2-2021-06" +# #checkov:skip=CKV_AWS_103: "ssl_policy for lb set in application_variables" + +# default_action { +# type = "forward" +# target_group_arn = aws_lb_target_group.target_group.arn +# } +# } + +# # # This will build on the core-vpc development account under platforms-development.modernisation-platform.service.justice.gov.uk, and route traffic back to example LB +# resource "aws_route53_record" "example" { +# provider = aws.core-vpc +# zone_id = data.aws_route53_zone.external.zone_id +# name = "${var.networking[0].application}.${var.networking[0].business-unit}-${local.environment}.modernisation-platform.service.justice.gov.uk" +# type = "A" + +# alias { +# name = aws_lb.external.dns_name +# zone_id = aws_lb.external.zone_id +# evaluate_target_health = true +# } +# } # Creation of a WAFv2 -resource "aws_wafv2_web_acl" "external" { - #checkov:skip=CKV2_AWS_31:Logging example commented out below, example is sound but no logging configuration for it to build. - name = "example-web-acl" - scope = "REGIONAL" - - default_action { - allow {} - } - - rule { - name = "AWS-AWSManagedRulesKnownBadInputsRuleSet" - priority = 1 - - override_action { - none {} - } - - statement { - managed_rule_group_statement { - name = "AWSManagedRulesKnownBadInputsRuleSet" - vendor_name = "AWS" - } - } - - visibility_config { - cloudwatch_metrics_enabled = false - metric_name = "friendly-rule-metric-name" - sampled_requests_enabled = false - } - } - - visibility_config { - cloudwatch_metrics_enabled = false - metric_name = "my-web-acl" - sampled_requests_enabled = false - } -} - -# Association code for WAFv2 to the LB -resource "aws_wafv2_web_acl_association" "web_acl_association_my_lb" { - resource_arn = aws_lb.external.arn - web_acl_arn = aws_wafv2_web_acl.external.arn -} +# resource "aws_wafv2_web_acl" "external" { +# #checkov:skip=CKV2_AWS_31:Logging example commented out below, example is sound but no logging configuration for it to build. +# name = "example-web-acl" +# scope = "REGIONAL" + +# default_action { +# allow {} +# } + +# rule { +# name = "AWS-AWSManagedRulesKnownBadInputsRuleSet" +# priority = 1 + +# override_action { +# none {} +# } + +# statement { +# managed_rule_group_statement { +# name = "AWSManagedRulesKnownBadInputsRuleSet" +# vendor_name = "AWS" +# } +# } + +# visibility_config { +# cloudwatch_metrics_enabled = false +# metric_name = "friendly-rule-metric-name" +# sampled_requests_enabled = false +# } +# } + +# visibility_config { +# cloudwatch_metrics_enabled = false +# metric_name = "my-web-acl" +# sampled_requests_enabled = false +# } +# } + +# # Association code for WAFv2 to the LB +# resource "aws_wafv2_web_acl_association" "web_acl_association_my_lb" { +# resource_arn = aws_lb.external.arn +# web_acl_arn = aws_wafv2_web_acl.external.arn +# } # Logging for WAF, it's commented out because it wouldn't build, however it's a basic example. @@ -183,165 +183,165 @@ resource "aws_wafv2_web_acl_association" "web_acl_association_my_lb" { ################################################################################# ######################### S3 Bucket required for logs ########################## ################################################################################# -module "s3-bucket-lb" { - #tfsec:ignore:aws-s3-enable-versioning - source = "github.com/ministryofjustice/modernisation-platform-terraform-s3-bucket?ref=568694e50e03630d99cb569eafa06a0b879a1239" #v7.1.0 - - bucket_prefix = "s3-bucket-example-lb" - versioning_enabled = false - bucket_policy = [data.aws_iam_policy_document.bucket_policy_lb.json] - - # Enable bucket to be destroyed when not empty - force_destroy = true - # Refer to the below section "Replication" before enabling replication - replication_enabled = false - # Below three variables and providers configuration are only relevant if 'replication_enabled' is set to true - replication_region = "eu-west-2" - # replication_role_arn = module.s3-bucket-replication-role.role.arn - providers = { - # Here we use the default provider Region for replication. Destination buckets can be within the same Region as the - # source bucket. On the other hand, if you need to enable cross-region replication, please contact the Modernisation - # Platform team to add a new provider for the additional Region. - aws.bucket-replication = aws - } - - lifecycle_rule = [ - { - id = "main" - enabled = "Enabled" - prefix = "" - - tags = { - rule = "log" - autoclean = "true" - } - - transition = [ - { - days = 90 - storage_class = "STANDARD_IA" - }, { - days = 365 - storage_class = "GLACIER" - } - ] - - expiration = { - days = 730 - } - - noncurrent_version_transition = [ - { - days = 90 - storage_class = "STANDARD_IA" - }, { - days = 365 - storage_class = "GLACIER" - } - ] - - noncurrent_version_expiration = { - days = 730 - } - } - ] - - tags = { Name = lower(format("s3-bucket-%s-%s-example", local.application_name, local.environment)) } -} - -data "aws_iam_policy_document" "bucket_policy_lb" { - statement { - effect = "Allow" - actions = [ - "s3:PutObject" - ] - resources = ["${module.s3-bucket-lb.bucket.arn}/test-lb/AWSLogs/*"] - principals { - type = "AWS" - identifiers = [data.aws_elb_service_account.default_lb.arn] - } - } - statement { - sid = "AWSLogDeliveryWrite" - - principals { - type = "Service" - identifiers = ["delivery.logs.amazonaws.com"] - } - - actions = [ - "s3:PutObject" - ] - - resources = ["${module.s3-bucket-lb.bucket.arn}/test-lb/AWSLogs/*"] - - condition { - test = "StringEquals" - variable = "s3:x-amz-acl" - - values = [ - "bucket-owner-full-control" - ] - } - } - - statement { - sid = "AWSLogDeliveryAclCheck" - - principals { - type = "Service" - identifiers = ["delivery.logs.amazonaws.com"] - } - - actions = [ - "s3:GetBucketAcl" - ] - - resources = [ - module.s3-bucket-lb.bucket.arn - ] - } -} - -data "aws_iam_policy_document" "s3-access-policy-lb" { - version = "2012-10-17" - statement { - sid = "" - effect = "Allow" - actions = [ - "sts:AssumeRole", - ] - principals { - type = "Service" - identifiers = [ - "rds.amazonaws.com", - "ec2.amazonaws.com", - ] - } - } -} - -data "aws_elb_service_account" "default_lb" {} +# module "s3-bucket-lb" { +# #tfsec:ignore:aws-s3-enable-versioning +# source = "github.com/ministryofjustice/modernisation-platform-terraform-s3-bucket?ref=568694e50e03630d99cb569eafa06a0b879a1239" #v7.1.0 + +# bucket_prefix = "s3-bucket-example-lb" +# versioning_enabled = false +# bucket_policy = [data.aws_iam_policy_document.bucket_policy_lb.json] + +# # Enable bucket to be destroyed when not empty +# force_destroy = true +# # Refer to the below section "Replication" before enabling replication +# replication_enabled = false +# # Below three variables and providers configuration are only relevant if 'replication_enabled' is set to true +# replication_region = "eu-west-2" +# # replication_role_arn = module.s3-bucket-replication-role.role.arn +# providers = { +# # Here we use the default provider Region for replication. Destination buckets can be within the same Region as the +# # source bucket. On the other hand, if you need to enable cross-region replication, please contact the Modernisation +# # Platform team to add a new provider for the additional Region. +# aws.bucket-replication = aws +# } + +# lifecycle_rule = [ +# { +# id = "main" +# enabled = "Enabled" +# prefix = "" + +# tags = { +# rule = "log" +# autoclean = "true" +# } + +# transition = [ +# { +# days = 90 +# storage_class = "STANDARD_IA" +# }, { +# days = 365 +# storage_class = "GLACIER" +# } +# ] + +# expiration = { +# days = 730 +# } + +# noncurrent_version_transition = [ +# { +# days = 90 +# storage_class = "STANDARD_IA" +# }, { +# days = 365 +# storage_class = "GLACIER" +# } +# ] + +# noncurrent_version_expiration = { +# days = 730 +# } +# } +# ] + +# tags = { Name = lower(format("s3-bucket-%s-%s-example", local.application_name, local.environment)) } +# } + +# data "aws_iam_policy_document" "bucket_policy_lb" { +# statement { +# effect = "Allow" +# actions = [ +# "s3:PutObject" +# ] +# resources = ["${module.s3-bucket-lb.bucket.arn}/test-lb/AWSLogs/*"] +# principals { +# type = "AWS" +# identifiers = [data.aws_elb_service_account.default_lb.arn] +# } +# } +# statement { +# sid = "AWSLogDeliveryWrite" + +# principals { +# type = "Service" +# identifiers = ["delivery.logs.amazonaws.com"] +# } + +# actions = [ +# "s3:PutObject" +# ] + +# resources = ["${module.s3-bucket-lb.bucket.arn}/test-lb/AWSLogs/*"] + +# condition { +# test = "StringEquals" +# variable = "s3:x-amz-acl" + +# values = [ +# "bucket-owner-full-control" +# ] +# } +# } + +# statement { +# sid = "AWSLogDeliveryAclCheck" + +# principals { +# type = "Service" +# identifiers = ["delivery.logs.amazonaws.com"] +# } + +# actions = [ +# "s3:GetBucketAcl" +# ] + +# resources = [ +# module.s3-bucket-lb.bucket.arn +# ] +# } +# } + +# data "aws_iam_policy_document" "s3-access-policy-lb" { +# version = "2012-10-17" +# statement { +# sid = "" +# effect = "Allow" +# actions = [ +# "sts:AssumeRole", +# ] +# principals { +# type = "Service" +# identifiers = [ +# "rds.amazonaws.com", +# "ec2.amazonaws.com", +# ] +# } +# } +# } + +# data "aws_elb_service_account" "default_lb" {} ################################################################################# #################### EC2 build for load balancer targets. ####################### ################################################################################# -resource "aws_instance" "lb_example_instance" { - #checkov:skip=CKV2_AWS_41:"IAM role is not implemented for this example EC2. SSH/AWS keys are not used either." - #checkov:skip=CKV_AWS_8: "Encryption not required for example instance" - # Specify the instance type and ami to be used (this is the Amazon free tier option) - instance_type = local.application_data.accounts[local.environment].instance_type - ami = local.application_data.accounts[local.environment].ami_image_id - vpc_security_group_ids = [aws_security_group.example_load_balancer_sg.id] - subnet_id = data.aws_subnet.private_subnets_a.id - monitoring = true - ebs_optimized = true - - metadata_options { - http_endpoint = "enabled" - http_tokens = "required" - } - tags = { Name = lower(format("ec2-%s-%s-example", local.application_name, local.environment)) } - depends_on = [aws_security_group.example_load_balancer_sg] -} \ No newline at end of file +# resource "aws_instance" "lb_example_instance" { +# #checkov:skip=CKV2_AWS_41:"IAM role is not implemented for this example EC2. SSH/AWS keys are not used either." +# #checkov:skip=CKV_AWS_8: "Encryption not required for example instance" +# # Specify the instance type and ami to be used (this is the Amazon free tier option) +# instance_type = local.application_data.accounts[local.environment].instance_type +# ami = local.application_data.accounts[local.environment].ami_image_id +# vpc_security_group_ids = [aws_security_group.example_load_balancer_sg.id] +# subnet_id = data.aws_subnet.private_subnets_a.id +# monitoring = true +# ebs_optimized = true + +# metadata_options { +# http_endpoint = "enabled" +# http_tokens = "required" +# } +# tags = { Name = lower(format("ec2-%s-%s-example", local.application_name, local.environment)) } +# depends_on = [aws_security_group.example_load_balancer_sg] +# } \ No newline at end of file diff --git a/terraform/environments/example/platform_versions.tf b/terraform/environments/example/platform_versions.tf index 63e0b5996b8..0805808572b 100644 --- a/terraform/environments/example/platform_versions.tf +++ b/terraform/environments/example/platform_versions.tf @@ -8,6 +8,10 @@ terraform { version = "~> 3.0" source = "hashicorp/http" } + external = { + source = "hashicorp/external" + version = "~> 2.3.0" # Use the latest version or specify your desired version + } cloudinit = { source = "hashicorp/cloudinit" version = "~> 2.3.0" # Use the latest version or specify your desired version diff --git a/terraform/environments/example/shield.tf b/terraform/environments/example/shield.tf index 284068d80a0..c08d550bee0 100644 --- a/terraform/environments/example/shield.tf +++ b/terraform/environments/example/shield.tf @@ -1,26 +1,39 @@ +# ########################################################################################## +# # ------------------------Comment out file if not required---------------------------------- +# ########################################################################################## -module "shield" { - source = "../../modules/shield_advanced" - providers = { - aws.modernisation-platform = aws.modernisation-platform - } - application_name = local.application_name - excluded_protections = local.application_data.accounts[local.environment].excluded_protections - resources = { - certificate_lb = { - arn = aws_lb.certificate_example_lb.arn - } - public_lb = { - action = "count", - arn = aws_lb.external.arn - } - } - waf_acl_rules = { - example = { - "action" = "count", - "name" = "example-count-rule", - "priority" = 0, - "threshold" = "1000" - } - } -} +# If you are getting errors with this code it might because there are protections that have not been disabled. login to the example account +# and go to the protected resources on the aws shield and remove any that have errors. once you have finished with what you are doing and are +# hashing out both the import and the resource you will need to remove the shield from the state file before running an apply. + + +# import { +# id = "c6f3ba81-c457-40f6-bd1f-30e777f60c27/FMManagedWebACLV2-shield_advanced_auto_remediate-1652297838425/REGIONAL" +# to = module.shield.aws_wafv2_web_acl.main +# } + +# module "shield" { +# source = "../../modules/shield_advanced" +# providers = { +# aws.modernisation-platform = aws.modernisation-platform +# } +# application_name = local.application_name +# excluded_protections = local.application_data.accounts[local.environment].excluded_protections +# resources = { +# certificate_lb = { +# arn = aws_lb.certificate_example_lb.arn +# } +# public_lb = { +# action = "count", +# arn = aws_lb.external.arn +# } +# } +# waf_acl_rules = { +# example = { +# "action" = "count", +# "name" = "example-count-rule", +# "priority" = 0, +# "threshold" = "1000" +# } +# } +# } diff --git a/terraform/environments/nomis/locals_preproduction.tf b/terraform/environments/nomis/locals_preproduction.tf index 7e13a6b139d..da5b3cb1162 100644 --- a/terraform/environments/nomis/locals_preproduction.tf +++ b/terraform/environments/nomis/locals_preproduction.tf @@ -105,11 +105,11 @@ locals { }) }) - # NOT-ACTIVE (green deployment) + # NOT-ACTIVE (green deployment) - for testing Combined Reporting preprod-nomis-web-b = merge(local.ec2_autoscaling_groups.web, { autoscaling_group = merge(local.ec2_autoscaling_groups.web.autoscaling_group, { - desired_capacity = 0 - max_size = 0 + desired_capacity = 1 + max_size = 1 initial_lifecycle_hooks = { "ready-hook" = { @@ -133,7 +133,7 @@ locals { }) user_data_cloud_init = merge(local.ec2_autoscaling_groups.web.user_data_cloud_init, { args = merge(local.ec2_autoscaling_groups.web.user_data_cloud_init.args, { - branch = "ncr/TM-596/preprod-nomis-link-test" + branch = "main" }) }) tags = merge(local.ec2_autoscaling_groups.web.tags, { diff --git a/terraform/environments/observability-platform/modules/grafana/athena-source/main.tf b/terraform/environments/observability-platform/modules/grafana/athena-source/main.tf index 178162769af..e725f26c5de 100644 --- a/terraform/environments/observability-platform/modules/grafana/athena-source/main.tf +++ b/terraform/environments/observability-platform/modules/grafana/athena-source/main.tf @@ -8,7 +8,7 @@ data "grafana_data_source" "this" { } resource "grafana_data_source" "this" { - type = "athena" + type = "grafana-athena-datasource" name = "${var.athena_workgroup}-${var.athena_database}" json_data_encoded = jsonencode({ defaultRegion = "eu-west-2" diff --git a/terraform/environments/observability-platform/modules/grafana/team/main.tf b/terraform/environments/observability-platform/modules/grafana/team/main.tf index ccdc31cc897..9aa4f77e2a9 100644 --- a/terraform/environments/observability-platform/modules/grafana/team/main.tf +++ b/terraform/environments/observability-platform/modules/grafana/team/main.tf @@ -56,17 +56,23 @@ resource "grafana_data_source_permission" "xray" { } data "grafana_data_source" "athena" { - for_each = { - for name, config in var.aws_accounts : name => config if config.athena_enabled - } - - name = "${each.key}-athena" + for_each = toset(flatten( + [ + for account_name, account_data in var.aws_accounts : + account_data.athena_enabled == true && try(account_data.athena_config, null) != null ? keys(account_data.athena_config) : [] + ] + )) + + name = each.key } resource "grafana_data_source_permission" "athena" { - for_each = { - for name, config in var.aws_accounts : name => config if config.athena_enabled - } + for_each = toset(flatten( + [ + for account_name, account_data in var.aws_accounts : + account_data.athena_enabled == true && try(account_data.athena_config, null) != null ? keys(account_data.athena_config) : [] + ] + )) datasource_uid = trimprefix(data.grafana_data_source.athena[each.key].id, "1:") diff --git a/terraform/environments/ppud/certificate_mgmt.tf b/terraform/environments/ppud/certificate_mgmt.tf index 31b5f1e355b..d7a3fae1819 100644 --- a/terraform/environments/ppud/certificate_mgmt.tf +++ b/terraform/environments/ppud/certificate_mgmt.tf @@ -24,7 +24,7 @@ resource "aws_lambda_function" "terraform_lambda_func_certificate_expiry_dev" { runtime = "python3.8" timeout = 30 reserved_concurrent_executions = 5 - code_signing_config_arn = "arn:aws:lambda:eu-west-2:075585660276:code-signing-config:csc-0c7136ccff2de748f" +# code_signing_config_arn = "arn:aws:lambda:eu-west-2:075585660276:code-signing-config:csc-0c7136ccff2de748f" depends_on = [aws_iam_role_policy_attachment.attach_lambda_policy_certificate_expiry_to_lambda_role_certificate_expiry_dev] environment { variables = { @@ -102,7 +102,7 @@ resource "aws_lambda_function" "terraform_lambda_func_certificate_expiry_uat" { runtime = "python3.8" timeout = 30 reserved_concurrent_executions = 5 - code_signing_config_arn = "arn:aws:lambda:eu-west-2:172753231260:code-signing-config:csc-0db408c5170a8eba6" +# code_signing_config_arn = "arn:aws:lambda:eu-west-2:172753231260:code-signing-config:csc-0db408c5170a8eba6" depends_on = [aws_iam_role_policy_attachment.attach_lambda_policy_certificate_expiry_to_lambda_role_certificate_expiry_uat] environment { variables = { @@ -180,7 +180,7 @@ resource "aws_lambda_function" "terraform_lambda_func_certificate_expiry_prod" { runtime = "python3.8" timeout = 30 reserved_concurrent_executions = 5 - code_signing_config_arn = "arn:aws:lambda:eu-west-2:817985104434:code-signing-config:csc-0bafee04a642a41c1" +# code_signing_config_arn = "arn:aws:lambda:eu-west-2:817985104434:code-signing-config:csc-0bafee04a642a41c1" depends_on = [aws_iam_role_policy_attachment.attach_lambda_policy_certificate_expiry_to_lambda_role_certificate_expiry_prod] environment { variables = { diff --git a/terraform/environments/ppud/iam.tf b/terraform/environments/ppud/iam.tf index 5143325d6b9..c3b3fda31d2 100644 --- a/terraform/environments/ppud/iam.tf +++ b/terraform/environments/ppud/iam.tf @@ -251,16 +251,7 @@ resource "aws_iam_policy" "iam_policy_for_lambda_alarm_suppression" { "cloudwatch:EnableAlarmActions" ], "Resource": [ - "arn:aws:cloudwatch:eu-west-2:817985104434:alarm:CPU-High-i-014bce95a85aaeede", - "arn:aws:cloudwatch:eu-west-2:817985104434:alarm:CPU-High-i-00cbccc46d25e77c6", - "arn:aws:cloudwatch:eu-west-2:817985104434:alarm:CPU-High-i-0dba6054c0f5f7a11", - "arn:aws:cloudwatch:eu-west-2:817985104434:alarm:CPU-High-i-0b5ef7cb90938fb82", - "arn:aws:cloudwatch:eu-west-2:817985104434:alarm:CPU-High-i-04bbb6312b86648be", - "arn:aws:cloudwatch:eu-west-2:817985104434:alarm:CPU-High-i-00413756d2dfcf6d2", - "arn:aws:cloudwatch:eu-west-2:817985104434:alarm:CPU-High-i-080498c4c9d25e6bd", - "arn:aws:cloudwatch:eu-west-2:817985104434:alarm:CPU-High-i-029d2b17679dab982", - "arn:aws:cloudwatch:eu-west-2:817985104434:alarm:CPU-High-70%-i-029d2b17679dab982", - "arn:aws:cloudwatch:eu-west-2:817985104434:alarm:CPU-High-90%-i-029d2b17679dab982" + "arn:aws:cloudwatch:eu-west-2:817985104434:alarm:*" ] }, { diff --git a/terraform/environments/ppud/lambda.tf b/terraform/environments/ppud/lambda.tf index 7bf61bde89c..a4ef6d33804 100644 --- a/terraform/environments/ppud/lambda.tf +++ b/terraform/environments/ppud/lambda.tf @@ -30,7 +30,7 @@ resource "aws_lambda_function" "terraform_lambda_func_stop" { runtime = "python3.9" depends_on = [aws_iam_role_policy_attachment.attach_lambda_policy_to_lambda_role] reserved_concurrent_executions = 5 - code_signing_config_arn = "arn:aws:lambda:eu-west-2:817985104434:code-signing-config:csc-0bafee04a642a41c1" +# code_signing_config_arn = "arn:aws:lambda:eu-west-2:817985104434:code-signing-config:csc-0bafee04a642a41c1" dead_letter_config { target_arn = aws_sqs_queue.lambda_queue_prod[0].arn } @@ -49,7 +49,7 @@ resource "aws_lambda_function" "terraform_lambda_func_start" { runtime = "python3.9" depends_on = [aws_iam_role_policy_attachment.attach_lambda_policy_to_lambda_role] reserved_concurrent_executions = 5 - code_signing_config_arn = "arn:aws:lambda:eu-west-2:817985104434:code-signing-config:csc-0bafee04a642a41c1" +# code_signing_config_arn = "arn:aws:lambda:eu-west-2:817985104434:code-signing-config:csc-0bafee04a642a41c1" dead_letter_config { target_arn = aws_sqs_queue.lambda_queue_prod[0].arn } @@ -200,7 +200,7 @@ resource "aws_lambda_function" "terraform_lambda_disable_cpu_alarm" { runtime = "python3.12" depends_on = [aws_iam_role_policy_attachment.attach_lambda_policy_alarm_suppression_to_lambda_role_alarm_suppression] reserved_concurrent_executions = 5 - code_signing_config_arn = "arn:aws:lambda:eu-west-2:817985104434:code-signing-config:csc-0bafee04a642a41c1" +# code_signing_config_arn = "arn:aws:lambda:eu-west-2:817985104434:code-signing-config:csc-0bafee04a642a41c1" dead_letter_config { target_arn = aws_sqs_queue.lambda_queue_prod[0].arn } @@ -221,7 +221,7 @@ resource "aws_lambda_function" "terraform_lambda_enable_cpu_alarm" { runtime = "python3.12" depends_on = [aws_iam_role_policy_attachment.attach_lambda_policy_alarm_suppression_to_lambda_role_alarm_suppression] reserved_concurrent_executions = 5 - code_signing_config_arn = "arn:aws:lambda:eu-west-2:817985104434:code-signing-config:csc-0bafee04a642a41c1" +# code_signing_config_arn = "arn:aws:lambda:eu-west-2:817985104434:code-signing-config:csc-0bafee04a642a41c1" dead_letter_config { target_arn = aws_sqs_queue.lambda_queue_prod[0].arn } @@ -254,7 +254,7 @@ resource "aws_lambda_function" "terraform_lambda_func_terminate_cpu_process_dev" timeout = 300 depends_on = [aws_iam_role_policy_attachment.attach_lambda_policy_cloudwatch_invoke_lambda_to_lambda_role_cloudwatch_invoke_lambda_dev] reserved_concurrent_executions = 5 - code_signing_config_arn = "arn:aws:lambda:eu-west-2:075585660276:code-signing-config:csc-0c7136ccff2de748f" +# code_signing_config_arn = "arn:aws:lambda:eu-west-2:075585660276:code-signing-config:csc-0c7136ccff2de748f" dead_letter_config { target_arn = aws_sqs_queue.lambda_queue_dev[0].arn } @@ -296,7 +296,7 @@ resource "aws_lambda_function" "terraform_lambda_func_terminate_cpu_process_uat" timeout = 300 depends_on = [aws_iam_role_policy_attachment.attach_lambda_policy_cloudwatch_invoke_lambda_to_lambda_role_cloudwatch_invoke_lambda_uat] reserved_concurrent_executions = 5 - code_signing_config_arn = "arn:aws:lambda:eu-west-2:172753231260:code-signing-config:csc-0db408c5170a8eba6" +# code_signing_config_arn = "arn:aws:lambda:eu-west-2:172753231260:code-signing-config:csc-0db408c5170a8eba6" dead_letter_config { target_arn = aws_sqs_queue.lambda_queue_uat[0].arn } @@ -338,7 +338,7 @@ resource "aws_lambda_function" "terraform_lambda_func_terminate_cpu_process_prod timeout = 300 depends_on = [aws_iam_role_policy_attachment.attach_lambda_policy_cloudwatch_invoke_lambda_to_lambda_role_cloudwatch_invoke_lambda_prod] reserved_concurrent_executions = 5 - code_signing_config_arn = "arn:aws:lambda:eu-west-2:817985104434:code-signing-config:csc-0bafee04a642a41c1" +# code_signing_config_arn = "arn:aws:lambda:eu-west-2:817985104434:code-signing-config:csc-0bafee04a642a41c1" dead_letter_config { target_arn = aws_sqs_queue.lambda_queue_prod[0].arn } @@ -380,7 +380,7 @@ resource "aws_lambda_function" "terraform_lambda_func_send_cpu_notification_dev" timeout = 300 depends_on = [aws_iam_role_policy_attachment.attach_lambda_policy_cloudwatch_invoke_lambda_to_lambda_role_cloudwatch_invoke_lambda_dev] reserved_concurrent_executions = 5 - code_signing_config_arn = "arn:aws:lambda:eu-west-2:075585660276:code-signing-config:csc-0c7136ccff2de748f" +# code_signing_config_arn = "arn:aws:lambda:eu-west-2:075585660276:code-signing-config:csc-0c7136ccff2de748f" dead_letter_config { target_arn = aws_sqs_queue.lambda_queue_dev[0].arn } @@ -422,7 +422,7 @@ resource "aws_lambda_function" "terraform_lambda_func_send_cpu_notification_uat" timeout = 300 depends_on = [aws_iam_role_policy_attachment.attach_lambda_policy_cloudwatch_invoke_lambda_to_lambda_role_cloudwatch_invoke_lambda_uat] reserved_concurrent_executions = 5 - code_signing_config_arn = "arn:aws:lambda:eu-west-2:172753231260:code-signing-config:csc-0db408c5170a8eba6" +# code_signing_config_arn = "arn:aws:lambda:eu-west-2:172753231260:code-signing-config:csc-0db408c5170a8eba6" dead_letter_config { target_arn = aws_sqs_queue.lambda_queue_uat[0].arn } @@ -464,7 +464,7 @@ resource "aws_lambda_function" "terraform_lambda_func_send_cpu_notification_prod timeout = 300 depends_on = [aws_iam_role_policy_attachment.attach_lambda_policy_cloudwatch_invoke_lambda_to_lambda_role_cloudwatch_invoke_lambda_prod] reserved_concurrent_executions = 5 - code_signing_config_arn = "arn:aws:lambda:eu-west-2:817985104434:code-signing-config:csc-0bafee04a642a41c1" +# code_signing_config_arn = "arn:aws:lambda:eu-west-2:817985104434:code-signing-config:csc-0bafee04a642a41c1" dead_letter_config { target_arn = aws_sqs_queue.lambda_queue_prod[0].arn } diff --git a/terraform/environments/ppud/lambda_scripts/disable_cpu_alarm.py b/terraform/environments/ppud/lambda_scripts/disable_cpu_alarm.py index 530fe7bba37..b6163622549 100644 --- a/terraform/environments/ppud/lambda_scripts/disable_cpu_alarm.py +++ b/terraform/environments/ppud/lambda_scripts/disable_cpu_alarm.py @@ -3,5 +3,5 @@ def lambda_handler(event, context): response = client.disable_alarm_actions( - AlarmNames=['CPU-High-i-014bce95a85aaeede','CPU-High-i-00cbccc46d25e77c6','CPU-High-i-0dba6054c0f5f7a11','CPU-High-i-0b5ef7cb90938fb82','CPU-High-i-04bbb6312b86648be','CPU-High-i-00413756d2dfcf6d2','CPU-High-i-080498c4c9d25e6bd','CPU-High-i-029d2b17679dab982','CPU-High-70%-i-029d2b17679dab982','CPU-High-90%-i-029d2b17679dab982'] + AlarmNames=['CPU-Utilisation-High-i-014bce95a85aaeede','CPU-Utilisation-High-i-00cbccc46d25e77c6','CPU-Utilisation-High-i-0dba6054c0f5f7a11','CPU-Utilisation-High-i-0b5ef7cb90938fb82','CPU-Utilisation-High-i-04bbb6312b86648be','CPU-Utilisation-High-i-00413756d2dfcf6d2','CPU-Utilisation-High-i-080498c4c9d25e6bd','CPU-High-i-029d2b17679dab982','CPU-High-70%-i-029d2b17679dab982','CPU-High-90%-i-029d2b17679dab982'] ) \ No newline at end of file diff --git a/terraform/environments/ppud/lambda_scripts/enable_cpu_alarm.py b/terraform/environments/ppud/lambda_scripts/enable_cpu_alarm.py index b9814fe95fe..e9947ed528a 100644 --- a/terraform/environments/ppud/lambda_scripts/enable_cpu_alarm.py +++ b/terraform/environments/ppud/lambda_scripts/enable_cpu_alarm.py @@ -3,5 +3,5 @@ def lambda_handler(event, context): response = client.enable_alarm_actions( - AlarmNames=['CPU-High-i-014bce95a85aaeede','CPU-High-i-00cbccc46d25e77c6','CPU-High-i-0dba6054c0f5f7a11','CPU-High-i-0b5ef7cb90938fb82','CPU-High-i-04bbb6312b86648be','CPU-High-i-00413756d2dfcf6d2','CPU-High-i-080498c4c9d25e6bd','CPU-High-i-029d2b17679dab982','CPU-High-70%-i-029d2b17679dab982','CPU-High-90%-i-029d2b17679dab982'] + AlarmNames=['CPU-Utilisation-High-i-014bce95a85aaeede','CPU-Utilisation-High-i-00cbccc46d25e77c6','CPU-Utilisation-High-i-0dba6054c0f5f7a11','CPU-Utilisation-High-i-0b5ef7cb90938fb82','CPU-Utilisation-High-i-04bbb6312b86648be','CPU-Utilisation-High-i-00413756d2dfcf6d2','CPU-Utilisation-High-i-080498c4c9d25e6bd','CPU-High-i-029d2b17679dab982','CPU-High-70%-i-029d2b17679dab982','CPU-High-90%-i-029d2b17679dab982'] ) \ No newline at end of file diff --git a/terraform/environments/ppud/s3.tf b/terraform/environments/ppud/s3.tf index 12aa86f0513..6a3db2e2d09 100644 --- a/terraform/environments/ppud/s3.tf +++ b/terraform/environments/ppud/s3.tf @@ -564,20 +564,15 @@ resource "aws_s3_bucket_public_access_block" "moj-log-files-uat" { restrict_public_buckets = true } -# Removed S3 bucket notification pending AWS ticket resolution - -/* resource "aws_s3_bucket_notification" "moj-log-files-uat" { count = local.is-preproduction == true ? 1 : 0 bucket = aws_s3_bucket.moj-log-files-uat[0].id - topic { - topic_arn = aws_sns_topic.cw_uat_alerts[0].arn + topic_arn = aws_sns_topic.s3_bucket_notifications_uat[0].arn events = ["s3:ObjectCreated:*"] filter_prefix = "alb-logs/" } } -*/ resource "aws_s3_bucket_lifecycle_configuration" "moj-log-files-uat" { count = local.is-preproduction == true ? 1 : 0 diff --git a/terraform/environments/ppud/sns.tf b/terraform/environments/ppud/sns.tf index b20363fd3e4..99788296c0e 100644 --- a/terraform/environments/ppud/sns.tf +++ b/terraform/environments/ppud/sns.tf @@ -31,14 +31,6 @@ resource "aws_sns_topic" "cw_uat_alerts" { name = "ppud-uat-cw-alerts" } -/* -resource "aws_sns_topic_policy" "sns_uat_policy" { - count = local.is-preproduction == true ? 1 : 0 - arn = aws_sns_topic.cw_uat_alerts[0].arn - policy = data.aws_iam_policy_document.sns_topic_policy_uat_ec2cw[0].json -} -*/ - resource "aws_sns_topic_subscription" "cw_uat_subscription" { count = local.is-preproduction == true ? 1 : 0 topic_arn = aws_sns_topic.cw_uat_alerts[0].arn @@ -76,21 +68,45 @@ resource "aws_sns_topic_policy" "sns_uat_policy" { "AWS:SourceOwner" : "data.aws_caller_identity.current.account_id" } } - }, + } + ] + }) +} + +# Pre-production - S3 Bucket Notification + +resource "aws_sns_topic" "s3_bucket_notifications_uat" { + # checkov:skip=CKV_AWS_26: "SNS topic encryption is not required as no sensitive data is processed through it" + count = local.is-preproduction == true ? 1 : 0 + name = "s3_bucket_notifications_uat" +} + +resource "aws_sns_topic_subscription" "s3_bucket_notifications_uat_subscription" { + count = local.is-preproduction == true ? 1 : 0 + topic_arn = aws_sns_topic.s3_bucket_notifications_uat[0].arn + protocol = "email" + endpoint = "PPUDAlerts@colt.net" +} + +resource "aws_sns_topic_policy" "s3_bucket_notifications_uat_policy" { + count = local.is-preproduction == true ? 1 : 0 + arn = aws_sns_topic.s3_bucket_notifications_uat[0].arn + + policy = jsonencode({ + Version = "2012-10-17", + ID = "s3_bucket_notifications_uat", + Statement = [ { - "Sid" : "S3-to-Publish-SNS", + "Sid" : "s3_bucket_notifications_uat_iam_policy", "Effect" : "Allow", "Principal" : { "Service" : "s3.amazonaws.com" }, "Action" : "SNS:Publish", - "Resource" : "aws_sns_topic.cw_uat_alerts[0].arn" + "Resource" : "aws_sns_topic.s3_bucket_notifications_uat[0].arn", "Condition" : { "ArnLike" : { "aws:SourceArn" : "arn:aws:s3:::moj-log-files-uat" - }, - "StringEquals" : { - "AWS:SourceAccount" : "172753231260" } } } diff --git a/terraform/environments/tribunals/dns-delegate-route53.tf b/terraform/environments/tribunals/dns-delegate-route53.tf index b2a6dad22e8..eb64f64ea4e 100644 --- a/terraform/environments/tribunals/dns-delegate-route53.tf +++ b/terraform/environments/tribunals/dns-delegate-route53.tf @@ -111,8 +111,8 @@ resource "aws_route53_record" "nginx_instances" { type = "A" alias { - name = "tribunals-nginx-1184258455.eu-west-1.elb.amazonaws.com." - zone_id = "Z32O12XQLNTSW2" + name = module.nginx_load_balancer[0].nginx_lb_arn + zone_id = module.nginx_load_balancer[0].nginx_lb_zone_id evaluate_target_health = false } } diff --git a/terraform/environments/tribunals/modules/nginx_ec2_pair/main.tf b/terraform/environments/tribunals/modules/nginx_ec2_pair/main.tf index d9977c3a288..618e580418c 100644 --- a/terraform/environments/tribunals/modules/nginx_ec2_pair/main.tf +++ b/terraform/environments/tribunals/modules/nginx_ec2_pair/main.tf @@ -99,12 +99,16 @@ resource "aws_s3_object" "sites_available" { bucket = aws_s3_bucket.nginx_config.id key = "sites-available/${each.value}" source = "${path.module}/sites-available/${each.value}" + # Use md5 to detect changes in the sites-available folder + etag = filemd5("${path.module}/sites-available/${each.value}") } resource "aws_s3_object" "nginx_conf" { bucket = aws_s3_bucket.nginx_config.id key = "nginx.conf" source = "${path.module}/nginx-conf/nginx.conf" + # Use md5 to detect changes in the nginx.conf file + etag = filemd5("${path.module}/nginx-conf/nginx.conf") } resource "aws_iam_role_policy_attachment" "s3_policy_attachment" { diff --git a/terraform/environments/tribunals/modules/nginx_ec2_pair/sites-available/ahmlr.gov.uk b/terraform/environments/tribunals/modules/nginx_ec2_pair/sites-available/ahmlr.gov.uk index 387fca89b3d..6bfd693dcfe 100644 --- a/terraform/environments/tribunals/modules/nginx_ec2_pair/sites-available/ahmlr.gov.uk +++ b/terraform/environments/tribunals/modules/nginx_ec2_pair/sites-available/ahmlr.gov.uk @@ -34,7 +34,7 @@ server { return 301 https://www.gov.uk/apply-land-registration-tribunal/overview; } location ~* ^/public { - return 301 https://landregistrationdivision.decisions.tribunals.gov.uk/$request_uri; + return 301 https://landregistrationdivision.decisions.tribunals.gov.uk/public/Search30May.aspx; } location ~* ^/Admin { return 301 https://landregistrationdivision.decisions.tribunals.gov.uk/$request_uri; diff --git a/terraform/environments/tribunals/modules/nginx_ec2_pair/sites-available/carestandardstribunal.gov.uk b/terraform/environments/tribunals/modules/nginx_ec2_pair/sites-available/carestandardstribunal.gov.uk index de57468cc87..29c9add877f 100644 --- a/terraform/environments/tribunals/modules/nginx_ec2_pair/sites-available/carestandardstribunal.gov.uk +++ b/terraform/environments/tribunals/modules/nginx_ec2_pair/sites-available/carestandardstribunal.gov.uk @@ -34,7 +34,7 @@ server { return 301 https://www.gov.uk/guidance/appeal-to-the-care-standards-tribunal; } location ~* ^/Public { - return 301 https://carestandards.decisions.tribunals.gov.uk/$request_uri; + return 301 https://carestandards.decisions.tribunals.gov.uk/Public/recentDecisions.aspx; } location ~* ^/images { return 301 https://carestandards.decisions.tribunals.gov.uk/$request_uri; diff --git a/terraform/environments/tribunals/modules/nginx_ec2_pair/sites-available/cicap.gov.uk b/terraform/environments/tribunals/modules/nginx_ec2_pair/sites-available/cicap.gov.uk index 61b921301b4..cc12f6af299 100644 --- a/terraform/environments/tribunals/modules/nginx_ec2_pair/sites-available/cicap.gov.uk +++ b/terraform/environments/tribunals/modules/nginx_ec2_pair/sites-available/cicap.gov.uk @@ -34,7 +34,7 @@ server { return 301 https://www.gov.uk/criminal-injuries-compensation-tribunal; } location ~* ^/Public { - return 301 https://cicap.decisions.tribunals.gov.uk/$request_uri; + return 301 https://cicap.decisions.tribunals.gov.uk/Public/publicsearch.aspx; } location ~* ^/images { return 301 https://cicap.decisions.tribunals.gov.uk/$request_uri; diff --git a/terraform/environments/tribunals/modules/nginx_ec2_pair/sites-available/consumercreditappeals.tribunals.gov.uk b/terraform/environments/tribunals/modules/nginx_ec2_pair/sites-available/consumercreditappeals.tribunals.gov.uk index af258b50a7b..36c3ce40855 100644 --- a/terraform/environments/tribunals/modules/nginx_ec2_pair/sites-available/consumercreditappeals.tribunals.gov.uk +++ b/terraform/environments/tribunals/modules/nginx_ec2_pair/sites-available/consumercreditappeals.tribunals.gov.uk @@ -26,6 +26,7 @@ server { listen 80; + listen 443; server_name consumercreditappeals.tribunals.gov.uk; @@ -33,7 +34,7 @@ server { return 301 https://www.gov.uk/courts-tribunals/upper-tribunal-tax-and-chancery-chamber; } location ~* ^/decisions.htm { - return 301 http://consumercreditappeals.decisions.tribunals.gov.uk; + return 301 https://consumercreditappeals.decisions.tribunals.gov.uk; } } diff --git a/terraform/environments/tribunals/modules/nginx_ec2_pair/sites-available/employmentappeals.gov.uk b/terraform/environments/tribunals/modules/nginx_ec2_pair/sites-available/employmentappeals.gov.uk index 2d0dcdfae49..2849de51aaa 100644 --- a/terraform/environments/tribunals/modules/nginx_ec2_pair/sites-available/employmentappeals.gov.uk +++ b/terraform/environments/tribunals/modules/nginx_ec2_pair/sites-available/employmentappeals.gov.uk @@ -34,7 +34,7 @@ server { return 301 https://www.gov.uk/courts-tribunals/employment-appeal-tribunal; } location ~* ^/Public { - return 301 https://employmentappeals.decisions.tribunals.gov.uk/$request_uri; + return 301 https://employmentappeals.decisions.tribunals.gov.uk/Public/Search.aspx; } location ~* ^/images { return 301 https://employmentappeals.decisions.tribunals.gov.uk/$request_uri; diff --git a/terraform/environments/tribunals/modules/nginx_ec2_pair/sites-available/informationtribunal.gov.uk b/terraform/environments/tribunals/modules/nginx_ec2_pair/sites-available/informationtribunal.gov.uk index f666c9bc609..d7bc760eef0 100644 --- a/terraform/environments/tribunals/modules/nginx_ec2_pair/sites-available/informationtribunal.gov.uk +++ b/terraform/environments/tribunals/modules/nginx_ec2_pair/sites-available/informationtribunal.gov.uk @@ -34,7 +34,7 @@ server { return 301 https://www.gov.uk/guidance/information-rights-appeal-against-the-commissioners-decision; } location ~* ^/Public { - return 301 https://informationrights.decisions.tribunals.gov.uk/$request_uri; + return 301 https://informationrights.decisions.tribunals.gov.uk/Public/search.aspx; } location ~* ^/images { return 301 https://informationrights.decisions.tribunals.gov.uk/$request_uri; diff --git a/terraform/environments/tribunals/modules/nginx_ec2_pair/sites-available/osscsc.gov.uk b/terraform/environments/tribunals/modules/nginx_ec2_pair/sites-available/osscsc.gov.uk index 170ee4c9afa..058ceacd320 100644 --- a/terraform/environments/tribunals/modules/nginx_ec2_pair/sites-available/osscsc.gov.uk +++ b/terraform/environments/tribunals/modules/nginx_ec2_pair/sites-available/osscsc.gov.uk @@ -34,7 +34,7 @@ server { return 301 https://www.gov.uk/courts-tribunals/upper-tribunal-administrative-appeals-chamber; } location ~* ^/aspx { - return 301 https://administrativeappeals.decisions.tribunals.gov.uk/$request_uri; + return 301 https://administrativeappeals.decisions.tribunals.gov.uk/Aspx/default.aspx; } location ~* ^/Decisions { return 301 https://administrativeappeals.decisions.tribunals.gov.uk/$request_uri; diff --git a/terraform/environments/tribunals/secrets.tf b/terraform/environments/tribunals/secrets.tf index 5f11d09b83b..29fbe552abd 100644 --- a/terraform/environments/tribunals/secrets.tf +++ b/terraform/environments/tribunals/secrets.tf @@ -99,17 +99,3 @@ data "aws_secretsmanager_secret_version" "tribunals_admin_site_credentials_secre depends_on = [aws_secretsmanager_secret_version.tribunals_admin_site_credentials_current] secret_id = data.aws_secretsmanager_secret.tribunals_admin_site_secret.id } - -resource "aws_secretsmanager_secret" "sftp_private_key" { - name = "private-key-sftp-upload" - recovery_window_in_days = 0 -} - -resource "aws_secretsmanager_secret_version" "sftp_private_key" { - secret_id = aws_secretsmanager_secret.sftp_private_key.id - secret_string = < +## Requirements + +| Name | Version | +|------|---------| +| [terraform](#requirement\_terraform) | ~> 1.5 | +| [aws](#requirement\_aws) | ~> 5.0 | + +## Providers + +| Name | Version | +|------|---------| +| [archive](#provider\_archive) | n/a | +| [aws](#provider\_aws) | ~> 5.0 | + +## Modules + +No modules. + +## Resources + +| Name | Type | +|------|------| +| [aws_cloudwatch_event_rule.ecs_restart_rule](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/cloudwatch_event_rule) | resource | +| [aws_cloudwatch_event_target.step_function_target](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/cloudwatch_event_target) | resource | +| [aws_iam_policy.lambda_ecs](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/iam_policy) | resource | +| [aws_iam_role.lambda_execution_role](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/iam_role) | resource | +| [aws_iam_role.step_function_role](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/iam_role) | resource | +| [aws_iam_role_policy.step_function_policy](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/iam_role_policy) | resource | +| [aws_iam_role_policy_attachment.lambda_ecs](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/iam_role_policy_attachment) | resource | +| [aws_lambda_function.calculate_wait_time](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/lambda_function) | resource | +| [aws_lambda_function.ecs_restart_handler](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/lambda_function) | resource | +| [aws_lambda_permission.allow_eventbridge](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/lambda_permission) | resource | +| [aws_sfn_state_machine.ecs_restart_state_machine](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/sfn_state_machine) | resource | +| [archive_file.lambda_function_calculate_wait_time_payload](https://registry.terraform.io/providers/hashicorp/archive/latest/docs/data-sources/file) | data source | +| [archive_file.lambda_function_ecs_restart_payload](https://registry.terraform.io/providers/hashicorp/archive/latest/docs/data-sources/file) | data source | +| [aws_caller_identity.current](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/data-sources/caller_identity) | data source | +| [aws_iam_policy_document.lambda_ecs](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/data-sources/iam_policy_document) | data source | +| [aws_region.current](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/data-sources/region) | data source | + +## Inputs + +| Name | Description | Type | Default | Required | +|------|-------------|------|---------|:--------:| +| [debug\_logging](#input\_debug\_logging) | Enable debug logging | `bool` | `false` | no | +| [restart\_day\_of\_the\_week](#input\_restart\_day\_of\_the\_week) | The day of the week to restart the ECS task | `string` | `"WEDNESDAY"` | no | +| [restart\_time](#input\_restart\_time) | The time at which to restart the ECS task | `string` | `"22:00"` | no | + +## Outputs + +No outputs. + diff --git a/terraform/modules/fargate_graceful_retirement/data.tf b/terraform/modules/fargate_graceful_retirement/data.tf new file mode 100644 index 00000000000..038d1e221a8 --- /dev/null +++ b/terraform/modules/fargate_graceful_retirement/data.tf @@ -0,0 +1,2 @@ +data "aws_caller_identity" "current" {} +data "aws_region" "current" {} diff --git a/terraform/modules/fargate_graceful_retirement/eventbridge.tf b/terraform/modules/fargate_graceful_retirement/eventbridge.tf new file mode 100644 index 00000000000..a8f7d3acd6a --- /dev/null +++ b/terraform/modules/fargate_graceful_retirement/eventbridge.tf @@ -0,0 +1,87 @@ +resource "aws_cloudwatch_event_rule" "ecs_restart_rule" { + name = "ecs_task_retirement_rul" + description = "Rule to catch AWS ECS Task Patching Retirement events" + + event_pattern = jsonencode({ + "detail-type" : ["AWS Health Event"], + "detail" : { + "eventTypeCode" : ["AWS_ECS_TASK_PATCHING_RETIREMENT"] + } + }) +} + +resource "aws_cloudwatch_event_target" "step_function_target" { + rule = aws_cloudwatch_event_rule.ecs_restart_rule.name + arn = aws_sfn_state_machine.ecs_restart_state_machine.arn + role_arn = aws_iam_role.step_function_role.arn +} + + +# test rule for all aws health events +resource "aws_cloudwatch_event_rule" "all_health_events" { + name = "all_health_events" + description = "Rule to catch all AWS Health events" + + event_pattern = jsonencode({ + "source" : ["aws.health"] + }) +} + +resource "aws_cloudwatch_log_group" "all_health_events" { + name = "/aws/health/all_health_events" +} + +data "aws_iam_policy_document" "all_health_events" { + statement { + effect = "Allow" + actions = [ + "logs:CreateLogStream" + ] + + resources = [ + "${aws_cloudwatch_log_group.all_health_events.arn}:*" + ] + + principals { + type = "Service" + identifiers = [ + "events.amazonaws.com", + "delivery.logs.amazonaws.com" + ] + } + } + statement { + effect = "Allow" + actions = [ + "logs:PutLogEvents" + ] + + resources = [ + "${aws_cloudwatch_log_group.all_health_events.arn}:*:*" + ] + + principals { + type = "Service" + identifiers = [ + "events.amazonaws.com", + "delivery.logs.amazonaws.com" + ] + } + + condition { + test = "ArnEquals" + values = [aws_cloudwatch_event_rule.all_health_events.arn] + variable = "aws:SourceArn" + } + } +} + +resource "aws_cloudwatch_log_resource_policy" "all_health_events" { + policy_document = data.aws_iam_policy_document.all_health_events.json + policy_name = "all-health-events-log-publishing-policy" +} + +resource "aws_cloudwatch_event_target" "all_health_events" { + rule = aws_cloudwatch_event_rule.all_health_events.name + arn = aws_cloudwatch_log_group.all_health_events.arn +} diff --git a/terraform/modules/fargate_graceful_retirement/files/calculate_wait_time/lambda_function.py b/terraform/modules/fargate_graceful_retirement/files/calculate_wait_time/lambda_function.py new file mode 100644 index 00000000000..93e71a1b7b9 --- /dev/null +++ b/terraform/modules/fargate_graceful_retirement/files/calculate_wait_time/lambda_function.py @@ -0,0 +1,56 @@ +import json +import datetime +def lambda_handler(event, context): + # Extract current event time + current_time_str = event.get('time', None) + + # If the event time is not available, return an error + if current_time_str is None: + return { + 'statusCode': 400, + 'error': 'Start time not available' + } + + # Parse the event time into a datetime object (example"2023-08-16T23:18:51Z") + time = datetime.datetime.strptime(current_time_str, '%Y-%m-%dT%H:%M:%SZ') + if os.environ.get('DEBUG_LOGGING', False): + print("[DEBUG] time from event:", time) + + # Define the desired restart time (as a string) + restart_time_str = event.get('restart_time', '22:00') + restart_day_of_the_week = event.get('restart_day_of_week', 'WEDNESDAY') + + if os.environ.get('DEBUG_LOGGING', False): + print("[DEBUG] Restart time:", restart_time_str) + print("[DEBUG] Restart day of the week:", restart_day_of_the_week) + + # get the next occurrence of the desired day of the week but after the current day + days_of_week = ['MONDAY', 'TUESDAY', 'WEDNESDAY', 'THURSDAY', 'FRIDAY', 'SATURDAY', 'SUNDAY'] + current_day_of_the_week = days_of_week[time.weekday()] + + if current_day_of_the_week == restart_day_of_the_week: + days_until_restart = 7 + if os.environ.get('DEBUG_LOGGING', False): + print("[DEBUG] Restart day is today, restarting next week instead.") + else: + # get the number of days until the next desired day of the week + days_until_restart = (days_of_week.index(restart_day_of_the_week) - days_of_week.index(current_day_of_the_week)) % 7 + + if os.environ.get('DEBUG_LOGGING', False): + print("[DEBUG] Days until restart:", days_until_restart) + + # get the desired restart time as a datetime object + restart_time = datetime.datetime.strptime(restart_time_str, '%H:%M') + + # add the number of days until the next desired day of the week + restart_time = datetime.datetime.combine(time.date(), restart_time.time()) + restart_time += datetime.timedelta(days=days_until_restart) + + if os.environ.get('DEBUG_LOGGING', False): + print("[DEBUG] Restart time:", restart_time) + + # Return the calculated timestamp + return { + 'statusCode': 200, + 'timestamp': restart_time.isoformat() + 'Z' # Assuming UTC + } diff --git a/terraform/modules/fargate_graceful_retirement/files/ecs_restart/lambda_function.py b/terraform/modules/fargate_graceful_retirement/files/ecs_restart/lambda_function.py new file mode 100644 index 00000000000..4e490ba8023 --- /dev/null +++ b/terraform/modules/fargate_graceful_retirement/files/ecs_restart/lambda_function.py @@ -0,0 +1,50 @@ +import json +import boto3 +import os + +def lambda_handler(event, context): + print("Event received:", json.dumps(event)) + + try: + # Create an ECS client using boto3 + ecs_client = boto3.client('ecs') + + # Extract the affected entities from the event + affected_entities = event['detail']['affectedEntities'] + + # Iterate over each affected entity + for entity in affected_entities: + # Get the entity value + entity_value = entity.get('entityValue') + if entity_value is not None: + # Extract cluster name and service name from the entity value + cluster_name = entity_value.split('|')[0] + service_name = entity_value.split('|')[1] + print("Cluster name:", cluster_name) + print("Service name:", service_name) + + print("Forcing new deployment for service:", service_name) + + # Force a new deployment for the specified service in the specified cluster + response = ecs_client.update_service( + cluster=cluster_name, + service=service_name, + forceNewDeployment=True + ) + if os.environ.get('DEBUG_LOGGING', False): + print("[DEBUG] Update service response:", response) + else: + print("No entity value found in the event") + + return { + 'statusCode': 200, + 'body': json.dumps('Handled ECS Task Patching Retirement') + 'restarted_services': affected_entities + } + + except Exception as e: + print("Error updating service:", e) + return { + 'statusCode': 500, + 'body': json.dumps('Error updating service') + } diff --git a/terraform/modules/fargate_graceful_retirement/lambda.tf b/terraform/modules/fargate_graceful_retirement/lambda.tf new file mode 100644 index 00000000000..5a5cbba6100 --- /dev/null +++ b/terraform/modules/fargate_graceful_retirement/lambda.tf @@ -0,0 +1,98 @@ +data "archive_file" "lambda_function_ecs_restart_payload" { + type = "zip" + source_dir = "${path.module}/files/ecs_restart" + output_path = "${path.module}/files/ecs_restart.zip" + excludes = ["ecs_restart.zip", "calculate_wait_time.zip"] +} + +resource "aws_iam_role" "lambda_execution_role" { + name = "lambda_execution_role" + + assume_role_policy = jsonencode({ + Version = "2012-10-17" + Statement = [ + { + Action = "sts:AssumeRole" + Effect = "Allow" + Principal = { + Service = "lambda.amazonaws.com" + } + }, + ] + }) + + managed_policy_arns = [ + "arn:aws:iam::aws:policy/service-role/AWSLambdaBasicExecutionRole", + ] +} + +data "aws_iam_policy_document" "lambda_ecs" { + statement { + actions = [ + "ecs:UpdateService", + "ecs:DescribeServices", + "ecs:ListServices" + ] + resources = ["arn:aws:ecs:${data.aws_region.current.name}:${data.aws_caller_identity.current.account_id}:service/*"] + } +} + +resource "aws_iam_policy" "lambda_ecs" { + name = "lambda_ecs_policy" + description = "IAM policy for Lambda to interact with ECS" + policy = data.aws_iam_policy_document.lambda_ecs.json +} + +resource "aws_iam_role_policy_attachment" "lambda_ecs" { + policy_arn = aws_iam_policy.lambda_ecs.arn + role = aws_iam_role.lambda_execution_role.name +} + + +resource "aws_lambda_function" "ecs_restart_handler" { + function_name = "ecs_restart_handler" + runtime = "python3.12" + handler = "lambda_function.lambda_handler" + role = aws_iam_role.lambda_execution_role.arn + + environment { + variables = { + DEBUG_LOGGING = var.debug_logging + } + } + + filename = data.archive_file.lambda_function_ecs_restart_payload.output_path + source_code_hash = data.archive_file.lambda_function_ecs_restart_payload.output_base64sha256 +} + +resource "aws_lambda_permission" "allow_eventbridge" { + statement_id = "AllowExecutionFromEventBridge" + action = "lambda:InvokeFunction" + function_name = aws_lambda_function.ecs_restart_handler.function_name + principal = "events.amazonaws.com" + source_arn = aws_cloudwatch_event_rule.ecs_restart_rule.arn +} + + +resource "aws_lambda_function" "calculate_wait_time" { + function_name = "calculate_wait_time" + runtime = "python3.12" + handler = "lambda_function.lambda_handler" + role = aws_iam_role.lambda_execution_role.arn + + environment { + variables = { + DEBUG_LOGGING = var.debug_logging + } + } + + filename = data.archive_file.lambda_function_calculate_wait_time_payload.output_path + source_code_hash = data.archive_file.lambda_function_calculate_wait_time_payload.output_base64sha256 +} + +data "archive_file" "lambda_function_calculate_wait_time_payload" { + type = "zip" + source_dir = "${path.module}/files/calculate_wait_time" + output_path = "${path.module}/files/calculate_wait_time.zip" + excludes = ["calculate_wait_time.zip", "ecs_restart.zip"] +} diff --git a/terraform/modules/fargate_graceful_retirement/required_providers.tf b/terraform/modules/fargate_graceful_retirement/required_providers.tf new file mode 100644 index 00000000000..f288605c066 --- /dev/null +++ b/terraform/modules/fargate_graceful_retirement/required_providers.tf @@ -0,0 +1,9 @@ +terraform { + required_providers { + aws = { + source = "hashicorp/aws" + version = "~> 5.0" + } + } + required_version = "~> 1.5" +} diff --git a/terraform/modules/fargate_graceful_retirement/step_functions.tf b/terraform/modules/fargate_graceful_retirement/step_functions.tf new file mode 100644 index 00000000000..704751375b6 --- /dev/null +++ b/terraform/modules/fargate_graceful_retirement/step_functions.tf @@ -0,0 +1,87 @@ +resource "aws_iam_role" "step_function_role" { + name = "step_function_execution_role" + + assume_role_policy = jsonencode({ + Version = "2012-10-17" + Statement = [ + { + Action = "sts:AssumeRole" + Effect = "Allow" + Principal = { + Service = "states.amazonaws.com" + } + }, + ] + }) +} + +resource "aws_iam_policy" "step_function_policy" { + name = "step_function_policy" + policy = jsonencode({ + Version = "2012-10-17" + Statement = [ + { + Effect = "Allow" + Action = "lambda:InvokeFunction", + Resource = [aws_lambda_function.ecs_restart_handler.arn, aws_lambda_function.calculate_wait_time.arn] + }, + { + Effect = "Allow" + Action = "logs:*", + Resource = "*" + } + ] + }) +} + +resource "aws_iam_role_policy_attachment" "step_function_policy_attachment" { + policy_arn = aws_iam_policy.step_function_policy.arn + role = aws_iam_role.step_function_role.name +} + +resource "aws_cloudwatch_log_group" "log_group_for_sfn" { + name = "/aws/states/ecs_restart_state_machine" +} + +resource "aws_sfn_state_machine" "ecs_restart_state_machine" { + name = "ecs_restart_state_machine" + role_arn = aws_iam_role.step_function_role.arn + + logging_configuration { + log_destination = "${aws_cloudwatch_log_group.log_group_for_sfn.arn}:*" + include_execution_data = var.debug_logging ? true : false + level = var.debug_logging ? "ALL" : "ERROR" + } + + definition = jsonencode({ + Comment : "State Machine to handle ECS Task Patching Retirement", + StartAt : "CalculateWaitTimestamp", + States : { + CalculateWaitTimestamp : { + Type : "Task", + Resource : aws_lambda_function.calculate_wait_time.arn, + Parameters : { + "time.$" : "$.time", # Pass the event time from the input + "restart_time" : var.restart_time + "restart_day_of_the_week" : var.restart_day_of_the_week + }, + ResultPath : "$.waitTimestamp", # Store the result in $.waitTimestamp + Next : "WaitUntilRestartTime" + }, + WaitUntilRestartTime : { + Type : "Wait", + TimestampPath : "$.waitTimestamp.timestamp", # Use the computed timestamp + Next : "InvokeLambdaFunction" + }, + InvokeLambdaFunction : { + Type : "Task", + Resource : "arn:aws:states:::lambda:invoke", + Parameters : { + "FunctionName" : aws_lambda_function.ecs_restart_handler.arn, + "Payload.$" : "$" + }, + End : true + } + } + }) +} diff --git a/terraform/modules/fargate_graceful_retirement/variables.tf b/terraform/modules/fargate_graceful_retirement/variables.tf new file mode 100644 index 00000000000..e3a3c3e9c68 --- /dev/null +++ b/terraform/modules/fargate_graceful_retirement/variables.tf @@ -0,0 +1,21 @@ +variable "restart_time" { + description = "The time at which to restart the ECS task" + type = string + default = "22:00" +} + +variable "restart_day_of_the_week" { + description = "The day of the week to restart the ECS task" + type = string + default = "WEDNESDAY" + validation { + condition = can(regex("^(MONDAY|TUESDAY|WEDNESDAY|THURSDAY|FRIDAY|SATURDAY|SUNDAY)$", var.restart_day_of_the_week)) + error_message = "The restart_day_of_the_week must be one of MONDAY, TUESDAY, WEDNESDAY, THURSDAY, FRIDAY, SATURDAY, or SUNDAY" + } +} + +variable "debug_logging" { + description = "Enable debug logging" + type = bool + default = false +}