module "default_tags" {
  source             = "git@github.com:theorchard/terraform-default-tags.git//?ref=2.0.0"
  environment        = var.environment
  application_family = var.application_family
  team_name          = var.team_name
  service_name       = var.service_name
}

provider "aws" {
  region = var.aws_region

  default_tags {
    tags = module.default_tags.tags
  }
}

terraform {
  backend "s3" {
    bucket  = "gdb-infra-dev-tfstate"
    key     = "gdb-infra-dev/atlantis/terraform.tfstate"
    region  = "us-east-1"
    encrypt = "true"
  }
}

locals {
  name_prefix = "dev-infra"
}

module "cross_account_access" {
  source            = "git@github.com:theorchard/terraform-atlantis.git//modules?ref=3.4.52"
  environment       = "dev"
  terrastate_bucket = "gdb-infra-dev-tfstate"
}

data "aws_caller_identity" "current" {}

data "aws_iam_role" "standard_exec" {
  name = "${var.environment}-infra-ecs_task_execution_role"
}

data "aws_ecs_cluster" "main" {
  cluster_name = "${var.environment}-infra-clu"
}

data "aws_vpc" "main" {
  tags = {
    Name = "${local.name_prefix}-jenkins-vpc"
  }
}

data "aws_subnets" "private" {
  filter {
    name   = "vpc-id"
    values = [data.aws_vpc.main.id]
  }
  tags = {
    Name = "${local.name_prefix}-jenkins-subnet-private-0*"
  }
}

data "aws_subnets" "public" {
  filter {
    name   = "vpc-id"
    values = [data.aws_vpc.main.id]
  }
  tags = {
    Name = "${local.name_prefix}-jenkins-subnet-public-0*"
  }
}

data "aws_acm_certificate" "delphi_domain" {
  domain   = "*.delphiplatform.io"
  statuses = ["ISSUED"]
}

data "aws_network_interface" "nlb_interface" {
  for_each = toset(data.aws_subnets.public.ids)

  filter {
    name   = "description"
    values = ["ELB ${aws_lb.atlantis_nlb.arn_suffix}"]
  }

  filter {
    name   = "subnet-id"
    values = [each.value]
  }
}

data "github_ip_ranges" "allowed_incoming_ranges" {}

module "custom_waf" {
  source            = "git@github.com:theorchard/terraform-aws-waf.git//?ref=2.0.2"
  environment       = var.environment
  service_name      = var.service_name
  aws_region        = var.aws_region
  count_waf_enabled = false
  block_waf_enabled = true
  excluded_rules = [
    "SizeRestrictions_BODY",
    "CrossSiteScripting_BODY",
  ]
}

moved {
  from = aws_wafv2_web_acl_logging_configuration.waf_block_web_acl_logging_configuration
  to   = module.custom_waf.aws_wafv2_web_acl_logging_configuration.waf[0]
}

data "aws_kms_key" "secrets_general_key" {
  key_id = "alias/${var.environment}/infra/secrets_general"
}

resource "aws_secretsmanager_secret" "atlantis_secret" {
  for_each = toset(var.secrets_manager_secret_names)

  name       = "infra/${var.environment}/atlantis/common/${each.key}"
  kms_key_id = data.aws_kms_key.secrets_general_key.id

  tags = {
    project                  = "Infrastructure",
    service                  = "Secrets Manager",
    plat_env_project_service = "DLP_CMN_INFRA_SM"
    terraformed              = true
  }
}

resource "aws_cloudwatch_log_group" "datadog_agent" {
  name              = "/ecs/${local.name_prefix}-datadog-agent"
  retention_in_days = 14

  tags = {
    project                  = "Infrastructure",
    service                  = "CloudWatch",
    plat_env_project_service = "DLP_CMN_INFRA_CW"
    terraformed              = true
  }
}

module "fargate_atlantis_task_def" {
  source = "git@github.com:theorchard/infrastructure.git//Terraform/modules/ecs/tasks/fargate_v3?ref=master"

  env_prefix      = var.environment
  project_group   = "infra"
  family          = "${var.environment}-infra-${var.service_name}"
  container_name  = var.service_name
  container_image = var.atlantis_image_uri
  cpu             = 2048
  memory          = 8192

  awslogs_retention = "180"

  healthcheck = {
    command     = ["CMD-SHELL", "curl -f http://localhost:${var.container_port}/ || exit 1"]
    retries     = 3
    timeout     = 5
    interval    = 30
    startPeriod = 5
  }

  datadog_enabled    = true
  dd_apm_enabled     = true
  dd_logs_injection  = true
  dd_container_image = "public.ecr.aws/datadog/agent:latest"
  dd_awslogs_group   = aws_cloudwatch_log_group.datadog_agent.name
  dd_env_vars = [
    {
      name  = "DD_DOGSTATSD_NON_LOCAL_TRAFFIC",
      value = true
    },
    {
      name  = "DD_APM_IGNORE_RESOURCES",
      value = "GET /"
    },
    {
      name  = "DD_TAGS",
      value = "service:atlantis application_family:devops"
    },
  ]
  dd_secrets = [
    {
      name      = "DD_API_KEY",
      valueFrom = aws_secretsmanager_secret.atlantis_secret["DD_API_KEY"].arn
    }
  ]

  task_role_arn = aws_iam_role.atlantis_task_role.arn
  exec_role_arn = data.aws_iam_role.standard_exec.arn

  env_vars = [
    {
      name  = "Environment",
      value = "dev"
    },
    {
      name  = "ATLANTIS_ATLANTIS_URL",
      value = "https://${var.atlantis_service_fqdn}"
    },
    // Need to create an atantis github user
    {
      name  = "ATLANTIS_GH_USER",
      value = "orchard_ci"
    },
    {
      name  = "ATLANTIS_REPO_ALLOWLIST",
      value = join(",", formatlist("github.com/%s", var.github_repositories))
    },
    {
      name  = "ATLANTIS_ALLOW_FORK_PRS",
      value = true
    },
    {
      name  = "ATLANTIS_WRITE_GIT_CREDS",
      value = true
    },
    {
      name  = "ATLANTIS_DEFAULT_TF_VERSION",
      value = "v${var.atlantis_terraform_version}"
    },
    {
      name = "ATLANTIS_AUTOPLAN_FILE_LIST",
      value = join(",", [
        "Terraform/infra/prod/*.tf*",
        "Terraform/infra/prod/billing_report/*.tf*",
        "Terraform/infra/dev/*.tf*",
        "Terraform/infra/dev/**/*.tf*",
        "Terraform/whitelist/prod/**/*.tf*",
        "Terraform/whitelist/uat/**/*.tf*",
        "Terraform/whitelist/legacy/**/*.tf*",
        "Terraform/whitelist/qa/**/*.tf*",
        "Terraform/whitelist/dev/**/*.tf*",
        "Terraform/whitelist/common_dev/*.tf*",
        "Terraform/core/prod/**/*.tf*",
        "Terraform/core/uat/**/*.tf*",
        "Terraform/core/qa/**/*.tf*",
        "Terraform/core/dev/**/*.tf*",
        "Terraform/core/common_dev/*.tf*",
        "Terraform/ecommerce/prod/**/*.tf*",
        "Terraform/ecommerce/dev/**/*.tf*",
        "Terraform/ecommerce/common_dev/*.tf*",
        "Terraform/security/prod/**/*.tf*",
        "Terraform/security/dev/**/*.tf*",
        "Terraform/prodhub/prod/**/*.tf*",
        "Terraform/prodhub/commond_dev/*.tf*",
        "Terraform/prodhub/qa/**/*.tf*",
        "Terraform/databricks/prod/**/*.tf*",
        "Terraform/databricks/dev/**/*.tf*",
        "Terraform/apollo_new/prod/**/*.tf*",
        "Terraform/apollo_new/uat/**/*.tf*",
        "Terraform/apollo_new/qa/**/*.tf*",
        "Terraform/apollo_new/stage/**/*.tf*",
        "Terraform/apollo_new/dev/**/*.tf*",
        "Terraform/apollo_new/common_dev/*.tf*",
        "Terraform/delphi/prod/**/*.tf*",
        "Terraform/delphi/common_gcp/*.tf*",
        "Terraform/delphi/qa/**/*.tf*",
        "Terraform/delphi/gcp-common/*.tf*",
        "Terraform/delphi/common_prod/*.tf*",
        "Terraform/delphi/stage/**/*.tf*",
        "Terraform/delphi/dev/**/*.tf*",
        "Terraform/delphi/common_dev/*.tf*",
        "Terraform/apollo/prod/**/*.tf*",
        "Terraform/apollo/dev/**/*.tf*",
        "Terraform/apollo/common_dev/*.tf*",
        "Terraform/Datadog/*.tf*",
        "Terraform/Datadog/delphi/*.tf*",
        "Terraform/sentry/**/*.tf*",
        "Terraform/infrastructure/**/*.tf*",
        "Terraform/datascience/dev/**/*.tf*",
        "Terraform/datascience/common_dev/*.tf*",
      ])
    },
    {
      name  = "ATLANTIS_SILENCE_NO_PROJECTS",
      value = true
    },
    {
      name = "ATLANTIS_REPO_CONFIG_JSON",
      value = jsonencode({
        repos = [
          {
            id                 = "/.*/"
            apply_requirements = ["approved", "mergeable"]
          }
        ]
      })
    },
    {
      name  = "ATLANTIS_ALLOW_COMMANDS",
      value = "version,plan,apply,unlock,approve_policies,import,state"
    },
    {
      name = "AWS_PROFILES",
      value = join(";", [
        "gdb-delphi-dev,arn:aws:iam::475275892927:role/cross_account_terraform_role",
        "gdb-delphi-prod,arn:aws:iam::323555055331:role/cross_account_terraform_role",
        "gdb-artistapp-dev,arn:aws:iam::714609459574:role/cross_account_terraform_role",
        "gdb-artistapp-prod,arn:aws:iam::923763343190:role/cross_account_terraform_role",
        "gdb-core-dev,arn:aws:iam::582412345909:role/cross_account_terraform_role",
        "gdb-core-prod,arn:aws:iam::885200931856:role/cross_account_terraform_role",
        "gdb-mct-dev,arn:aws:iam::925412343888:role/cross_account_terraform_role",
        "gdb-mct-prod,arn:aws:iam::046770693006:role/cross_account_terraform_role",
        "gdb-databricks-dev,arn:aws:iam::745243859288:role/cross_account_terraform_role",
        "gdb-databricks-prod,arn:aws:iam::807702934854:role/cross_account_terraform_role",
        "gdb-infra-dev,arn:aws:iam::483193324480:role/cross_account_terraform_role",
        "gdb-infra-prod,arn:aws:iam::890208661333:role/cross_account_terraform_role",
        "gdb-apollo-dev,arn:aws:iam::725764004555:role/cross_account_terraform_role",
        "gdb-apollo-prod,arn:aws:iam::801577982711:role/cross_account_terraform_role",
        "gdb-prodhub-dev,arn:aws:iam::703740486246:role/cross_account_terraform_role",
        "gdb-datasci-dev,arn:aws:iam::313114233826:role/cross_account_terraform_role",
        "gdb-whitelist-legacy,arn:aws:iam::322697034778:role/cross_account_terraform_role,eu-west-1",
        "gdb-whitelist-dev,arn:aws:iam::814622134907:role/cross_account_terraform_role",
        "gdb-whitelist-prod,arn:aws:iam::009424872958:role/cross_account_terraform_role",
        "gdb-smu-dev,arn:aws:iam::724514206585:role/cross_account_terraform_role",
        "gdb-smu-prod,arn:aws:iam::809878276326:role/cross_account_terraform_role",
      ])
    },
    {
      name  = "TF_PLUGIN_CACHE_MAY_BREAK_DEPENDENCY_LOCK_FILE"
      value = "true"
    },
    {
      name  = "ATLANTIS_HIDE_PREV_PLAN_COMMENTS"
      value = "true"
    },
  ]
  secrets = [
    {
      name      = "ATLANTIS_GH_WEBHOOK_SECRET",
      valueFrom = aws_secretsmanager_secret.atlantis_secret["GH_WEBHOOK_SECRET"].arn
    },
    {
      name      = "ATLANTIS_GH_TOKEN",
      valueFrom = aws_secretsmanager_secret.atlantis_secret["GH_TOKEN"].arn
    },
    {
      name      = "DD_API_KEY",
      valueFrom = aws_secretsmanager_secret.atlantis_secret["DD_API_KEY"].arn
    },
    {
      name      = "DD_APP_KEY",
      valueFrom = aws_secretsmanager_secret.atlantis_secret["DD_APP_KEY"].arn
    },
    {
      name      = "SENTRY_AUTH_TOKEN",
      valueFrom = aws_secretsmanager_secret.atlantis_secret["SENTRY_API_KEY"].arn
    },
    {
      name      = "FIVETRAN_APIKEY"
      valueFrom = aws_secretsmanager_secret.atlantis_secret["FIVETRAN_APIKEY"].arn
    },
    {
      name      = "FIVETRAN_APISECRET",
      valueFrom = aws_secretsmanager_secret.atlantis_secret["FIVETRAN_APISECRET"].arn
    },
  ]

  port_mappings = [
    {
      containerPort = var.container_port
      hostPort      = var.container_port
      protocol      = "tcp"
    }
  ]

  common_tags = {
    project                  = "Atlantis",
    service                  = "ECS",
    plat_env_project_service = "DLP_CMN_INFRA_ECS"
    application_family       = "devops"
    terraformed              = true
  }
}

module "fargate_atlantis_service" {
  source = "git@github.com:theorchard/infrastructure.git//Terraform/modules/ecs/services/service_taskless_v3?ref=master"

  env_prefix    = var.environment
  project       = var.service_name
  project_group = "infra"

  cluster_arn   = data.aws_ecs_cluster.main.arn
  desired_count = 1

  security_groups = [aws_security_group.atlantis_task_firewall.id]
  subnets         = data.aws_subnets.private.ids
  vpc_id          = data.aws_vpc.main.id

  task_definition_arn = module.fargate_atlantis_task_def.arn

  enable_ecs_managed_tags = true

  capacity_provider_strategy = {
    a = {
      base              = 1
      weight            = 1
      capacity_provider = "FARGATE"
    },
  }

  tg_arn         = aws_lb_target_group.atlantis_alb_target_group.arn
  container_port = var.container_port

  common_tags = {
    project                  = "Atlantis",
    service                  = "ECS",
    plat_env_project_service = "DLP_CMN_INFRA_ECS"
    application_family       = "devops"
    terraformed              = true
  }
}

resource "aws_lb" "atlantis_alb" {
  name                       = "${var.environment}-${var.service_name}"
  internal                   = true
  load_balancer_type         = "application"
  security_groups            = [aws_security_group.atlantis_lb_firewall.id]
  subnets                    = data.aws_subnets.private.ids
  enable_deletion_protection = false

  access_logs {
    bucket  = var.load_balancer_access_logs_s3_bucket_name
    prefix  = "elb/${var.environment}-${var.service_name}"
    enabled = true
  }

  tags = {
    environment              = var.environment
    service_name             = var.service_name
    terraformed              = true
    plat_env_project_service = "DLP_CMN_INFRA_LB"
  }
}

resource "aws_lb_listener" "atlantis_alb_listener" {
  load_balancer_arn = aws_lb.atlantis_alb.arn
  port              = "443"
  protocol          = "HTTPS"
  ssl_policy        = "ELBSecurityPolicy-TLS-1-2-2017-01"
  certificate_arn   = data.aws_acm_certificate.delphi_domain.arn

  default_action {
    type             = "forward"
    target_group_arn = aws_lb_target_group.atlantis_alb_target_group.arn
  }
}

resource "aws_lb_target_group" "atlantis_alb_target_group" {
  name        = "${var.environment}-${var.service_name}-tg"
  port        = var.container_port
  protocol    = "HTTP"
  target_type = "ip"
  vpc_id      = data.aws_vpc.main.id
}

resource "aws_security_group" "atlantis_lb_firewall" {
  name        = "${var.environment}_${var.service_name}_lb_sg"
  description = "Allow TLS inbound traffic from github"
  vpc_id      = data.aws_vpc.main.id
  ingress {
    description      = "Allow inbound traffic from GitHub"
    from_port        = 443
    to_port          = 443
    protocol         = "tcp"
    cidr_blocks      = data.github_ip_ranges.allowed_incoming_ranges.hooks_ipv4
    ipv6_cidr_blocks = data.github_ip_ranges.allowed_incoming_ranges.hooks_ipv6
  }

  ingress {
    description = "Allow additional CIDR blocks"
    from_port   = 443
    to_port     = 443
    protocol    = "tcp"
    cidr_blocks = var.additional_allowed_cidr_blocks
  }

  ingress {
    description = "Allow healthcheck from NLB"
    from_port   = 443
    to_port     = 443
    protocol    = "tcp"
    cidr_blocks = formatlist("%s/32", [for eni in data.aws_network_interface.nlb_interface : eni.private_ip])
  }

  egress {
    from_port       = var.container_port
    to_port         = var.container_port
    protocol        = "tcp"
    security_groups = [aws_security_group.atlantis_task_firewall.id]
  }

  tags = {
    "eiso-exception" = "aws.08.30"
  }
}

resource "aws_security_group" "atlantis_task_firewall" {
  name        = "${var.environment}_${var.service_name}_task_sg"
  description = "Allow traffic from atlantis LB to fargate task"
  vpc_id      = data.aws_vpc.main.id
  tags = {
    "eiso-exception" = "aws.08.30"
  }
}

resource "aws_security_group_rule" "atlantis_task_allow_inbound_from_lb" {
  type                     = "ingress"
  from_port                = var.container_port
  to_port                  = var.container_port
  protocol                 = "tcp"
  source_security_group_id = aws_security_group.atlantis_lb_firewall.id
  security_group_id        = aws_security_group.atlantis_task_firewall.id
}

resource "aws_security_group_rule" "atlantis_task_allow_outbound" {
  type              = "egress"
  from_port         = 0
  to_port           = 0
  protocol          = "-1"
  cidr_blocks       = ["0.0.0.0/0"]
  security_group_id = aws_security_group.atlantis_task_firewall.id
}

resource "aws_eip" "nlb_ip" {
  for_each = toset(data.aws_subnets.public.ids)

  tags = {
    Name                     = "${var.environment}-${var.service_name}-${each.value}-eip"
    environment              = var.environment
    service_name             = var.service_name
    terraformed              = true
    plat_env_project_service = "DLP_CMN_INFRA_LB"
  }
}

# Provision an NLB in front of the ALB to provide static IP addresses for use in firewall configuration
resource "aws_lb" "atlantis_nlb" {
  name                       = "${var.environment}-${var.service_name}-nlb"
  internal                   = false
  load_balancer_type         = "network"
  enable_deletion_protection = false

  access_logs {
    bucket  = var.load_balancer_access_logs_s3_bucket_name
    prefix  = "elb/${var.environment}-${var.service_name}-nlb"
    enabled = true
  }

  dynamic "subnet_mapping" {
    for_each = data.aws_subnets.public.ids
    content {
      subnet_id     = subnet_mapping.value
      allocation_id = aws_eip.nlb_ip[subnet_mapping.value].allocation_id
    }
  }

  tags = {
    environment              = var.environment
    service_name             = var.service_name
    terraformed              = true
    plat_env_project_service = "DLP_CMN_INFRA_LB"
  }
}

resource "aws_lb_listener" "atlantis_nlb_listener" {
  load_balancer_arn = aws_lb.atlantis_nlb.arn
  port              = "443"
  protocol          = "TCP"

  default_action {
    type             = "forward"
    target_group_arn = aws_lb_target_group.atlantis_nlb_target_group.arn
  }
}

resource "aws_lb_target_group" "atlantis_nlb_target_group" {
  name        = "${var.environment}-${var.service_name}-nlb-tg"
  port        = 443
  protocol    = "TCP"
  target_type = "alb"
  vpc_id      = data.aws_vpc.main.id

  health_check {
    protocol            = "HTTPS"
    interval            = 10
    healthy_threshold   = 2
    unhealthy_threshold = 3
    timeout             = 5
    matcher             = "200"
  }
}

resource "aws_lb_target_group_attachment" "atlantis_nlb_target_group_attachment" {
  depends_on       = [aws_lb_listener.atlantis_alb_listener]
  target_group_arn = aws_lb_target_group.atlantis_nlb_target_group.arn
  target_id        = aws_lb.atlantis_alb.arn
}

resource "aws_wafv2_web_acl_association" "waf_association" {
  resource_arn = aws_lb.atlantis_alb.arn
  web_acl_arn  = module.custom_waf.waf_blocking_arn_output
}
