|
| 1 | +## |
| 2 | +# ECS |
| 3 | +## |
| 4 | +locals { |
| 5 | + # Take the exported task container definition |
| 6 | + # and override some parts of it, so that it doesn't fall out of sync |
| 7 | + organisations_sync_container_definitions = merge( |
| 8 | + module.ecs_service.task_container_definition, |
| 9 | + { |
| 10 | + name = "forms-admin_organisations_sync", |
| 11 | + command = ["rake", "organisations:fetch"] |
| 12 | + logConfiguration = { |
| 13 | + logDriver = "awslogs", |
| 14 | + options = { |
| 15 | + awslogs-group = module.ecs_service.application_log_group_name, |
| 16 | + awslogs-region = "eu-west-2", |
| 17 | + awslogs-stream-prefix = "forms-admin-${var.env_name}-organisations-sync" |
| 18 | + } |
| 19 | + }, |
| 20 | + } |
| 21 | + ) |
| 22 | +} |
| 23 | + |
| 24 | +resource "aws_ecs_task_definition" "orgs_cron_job" { |
| 25 | + count = var.enable_organisations_sync ? 1 : 0 |
| 26 | + |
| 27 | + family = "${var.env_name}_forms-admin_organisations_sync" |
| 28 | + container_definitions = jsonencode([local.organisations_sync_container_definitions]) |
| 29 | + |
| 30 | + execution_role_arn = module.ecs_service.task_definition.execution_role_arn |
| 31 | + task_role_arn = module.ecs_service.task_definition.task_role_arn |
| 32 | + requires_compatibilities = module.ecs_service.task_definition.requires_compatibilities |
| 33 | + cpu = module.ecs_service.task_definition.cpu |
| 34 | + memory = module.ecs_service.task_definition.memory |
| 35 | + network_mode = "awsvpc" |
| 36 | + track_latest = true |
| 37 | + |
| 38 | + runtime_platform { |
| 39 | + operating_system_family = "LINUX" |
| 40 | + cpu_architecture = "ARM64" |
| 41 | + } |
| 42 | +} |
| 43 | + |
| 44 | +## |
| 45 | +# EventBridge |
| 46 | +## |
| 47 | +resource "aws_cloudwatch_event_rule" "sync_orgs_cron_job" { |
| 48 | + count = var.enable_organisations_sync ? 1 : 0 |
| 49 | + |
| 50 | + name = "${var.env_name}-forms-admin-orgs-sync-cron" |
| 51 | + description = "Trigger the forms-admin organisations synchronisation on a schedule" |
| 52 | + schedule_expression = "cron(30 11 * * 2 *)" # 11:30AM every Tuesday. In office hours so that we can respond to failures |
| 53 | +} |
| 54 | + |
| 55 | +resource "aws_cloudwatch_event_target" "ecs_org_sync_job" { |
| 56 | + count = var.enable_organisations_sync ? 1 : 0 |
| 57 | + |
| 58 | + arn = var.ecs_cluster_arn |
| 59 | + rule = aws_cloudwatch_event_rule.sync_orgs_cron_job[0].name |
| 60 | + role_arn = aws_iam_role.ecs_orgs_cron_scheduler[0].arn |
| 61 | + |
| 62 | + ecs_target { |
| 63 | + task_definition_arn = "arn:aws:ecs:eu-west-2:${data.aws_caller_identity.current.account_id}:task-definition/${aws_ecs_task_definition.orgs_cron_job[0].family}" |
| 64 | + launch_type = "FARGATE" |
| 65 | + platform_version = "1.4.0" |
| 66 | + |
| 67 | + network_configuration { |
| 68 | + assign_public_ip = false |
| 69 | + security_groups = module.ecs_service.service.network_configuration[0].security_groups |
| 70 | + subnets = module.ecs_service.service.network_configuration[0].subnets |
| 71 | + } |
| 72 | + } |
| 73 | + |
| 74 | + dead_letter_config { |
| 75 | + arn = var.eventbridge_dead_letter_queue_arn |
| 76 | + } |
| 77 | +} |
| 78 | + |
| 79 | +## Monitor for failure |
| 80 | +resource "aws_cloudwatch_event_rule" "sync_orgs_cron_job_failed" { |
| 81 | + count = var.enable_organisations_sync ? 1 : 0 |
| 82 | + |
| 83 | + name = "${var.env_name}-forms-admin-org-sync-failed" |
| 84 | + description = "Trigger when the organisations sync job has exited with a non-zero exit code" |
| 85 | + |
| 86 | + event_pattern = jsonencode({ |
| 87 | + source = ["aws.ecs"] |
| 88 | + detail-type = ["ECS Task State Change"] |
| 89 | + resources = [ |
| 90 | + { |
| 91 | + wildcard : "arn:aws:ecs:eu-west-2:${data.aws_caller_identity.current.account_id}:task/*" |
| 92 | + } |
| 93 | + ] |
| 94 | + |
| 95 | + detail = { |
| 96 | + lastStatus = ["STOPPED"] |
| 97 | + containers = { |
| 98 | + name = [local.organisations_sync_container_definitions.name] |
| 99 | + exitCode = [{ "anything-but" : [0] }] |
| 100 | + } |
| 101 | + } |
| 102 | + }) |
| 103 | +} |
| 104 | + |
| 105 | +resource "aws_cloudwatch_event_target" "sync_orgs_cron_job_alert_message" { |
| 106 | + count = var.enable_organisations_sync ? 1 : 0 |
| 107 | + |
| 108 | + rule = aws_cloudwatch_event_rule.sync_orgs_cron_job_failed[0].name |
| 109 | + |
| 110 | + # defined in 'environment' module. Sends alarms/errors via ZenDesk |
| 111 | + arn = var.zendesk_sns_topic_arn |
| 112 | + |
| 113 | + input_transformer { |
| 114 | + input_template = <<EOF |
| 115 | + { |
| 116 | + "title": "WARNING: Synchronising organisations from GOV.UK has failed.", |
| 117 | + "description": "GOV.UK Forms has a scheduled ECS task to sync our organisations from GOV.UK. When this task fails an email is sent to Zendesk.", |
| 118 | + "next-steps": { |
| 119 | + "1": "Navigate to Splunk: https://gds.splunkcloud.com/en-GB/app/gds-543-forms/search.", |
| 120 | + "2": "Search for index=gds_dsp_production_forms log_stream=forms-admin-production-organisations-sync/forms-admin_organisations_sync/*. Use the 'Today' date-time preset to find today's logs.", |
| 121 | + "3": "Review logs for errors." |
| 122 | + } |
| 123 | + } |
| 124 | + EOF |
| 125 | + } |
| 126 | + |
| 127 | + dead_letter_config { |
| 128 | + arn = var.eventbridge_dead_letter_queue_arn |
| 129 | + } |
| 130 | +} |
| 131 | + |
| 132 | +## |
| 133 | +# IAM |
| 134 | +## |
| 135 | +resource "aws_iam_role" "ecs_orgs_cron_scheduler" { |
| 136 | + count = var.enable_organisations_sync ? 1 : 0 |
| 137 | + |
| 138 | + name = "${var.env_name}-forms-admin-orgs-ecs-cron-scheduler" |
| 139 | + |
| 140 | + assume_role_policy = jsonencode({ |
| 141 | + Version = "2012-10-17" |
| 142 | + Statement = [ |
| 143 | + { |
| 144 | + Action = "sts:AssumeRole" |
| 145 | + Effect = "Allow" |
| 146 | + Principal = { |
| 147 | + Service = "events.amazonaws.com" |
| 148 | + } |
| 149 | + } |
| 150 | + ] |
| 151 | + }) |
| 152 | +} |
| 153 | + |
| 154 | +resource "aws_iam_role_policy_attachment" "ecs_orgs_events_policy" { |
| 155 | + count = var.enable_organisations_sync ? 1 : 0 |
| 156 | + |
| 157 | + policy_arn = "arn:aws:iam::aws:policy/service-role/AmazonEC2ContainerServiceEventsRole" |
| 158 | + role = aws_iam_role.ecs_orgs_cron_scheduler[0].name |
| 159 | +} |
0 commit comments