data "aws_vpc" "current_vpc" {
  id = var.vpc_id
}

data "aws_caller_identity" "current" {
}

# Get the latest CentOS AMI id
data "aws_ami" "centos" {
  most_recent = true

  owners = [
    "679593333241",
  ]

  filter {
    name = "virtualization-type"

    values = [
      "hvm",
    ]
  }

  filter {
    name = "name"

    values = [
      "CentOS Linux 7 x86_64 HVM EBS ENA 1901*",
    ]
  }
}

# User data for instances
data "template_file" "instance_user_data" {
  template = file("${path.root}/user_data.tpl")
}

data "aws_acm_certificate" "acm_certificate" {
  domain   = var.acm_domain
  statuses = ["ISSUED"]
}

data "aws_iam_policy_document" "assume_instance_role" {
  statement {
    actions = [
      "sts:AssumeRole",
    ]

    principals {
      type = "Service"
      identifiers = [
        "ec2.amazonaws.com",
      ]
    }
  }
}

# Get private subnets for default allow rules
data "aws_subnet_ids" "private_subnet_ids" {
  vpc_id = data.aws_vpc.current_vpc.id

  filter {
    name = "tag:tier"

    values = [
      "private",
    ]
  }
}

data "aws_subnet" "private_subnets" {
  count = length(data.aws_subnet_ids.private_subnet_ids.ids)
  id    = element(local.subnet_ids_list, count.index)
}

# Security group and rules for instances
# Network load balancers do not have security groups, so access to services is controlled by the instance
# security group, as the NLB is transparent and traffic appears to originate from client IP addresses
resource "aws_security_group" "instance_security_group" {
  name        = "${var.environment}-${var.service_name}-instance-security-group"
  description = "Instance security group for ${var.environment}-${var.service_name}"
  vpc_id      = var.vpc_id

  tags = {
    environment  = var.environment
    service_name = var.service_name
    terraformed  = true
  }
}

resource "aws_security_group_rule" "allow_ssh" {
  type              = "ingress"
  from_port         = var.ssh_port
  to_port           = var.ssh_port
  protocol          = "TCP"
  cidr_blocks       = var.ssh_ingress_cidr_blocks
  security_group_id = aws_security_group.instance_security_group.id
}

resource "aws_security_group_rule" "allow_udp" {
  count     = var.udp_plugin_enabled ? 1 : 0
  type      = "ingress"
  from_port = 5160
  to_port   = 5160
  protocol  = "UDP"
  cidr_blocks = concat(
    var.udp_allow_cidr_blocks,
    data.aws_subnet.private_subnets.*.cidr_block,
  )
  security_group_id = aws_security_group.instance_security_group.id
}

resource "aws_security_group_rule" "allow_http" {
  count     = var.http_plugin_enabled ? 1 : 0
  type      = "ingress"
  from_port = 8888
  to_port   = 8888
  protocol  = "TCP"
  cidr_blocks = concat(
    var.tcp_8888_allow_cidr_blocks,
    data.aws_subnet.private_subnets.*.cidr_block,
  )

  security_group_id = aws_security_group.instance_security_group.id
}

resource "aws_security_group_rule" "allow_in_forward" {
  type      = "ingress"
  from_port = 24224
  to_port   = 24224
  protocol  = "TCP"
  cidr_blocks = concat(
    var.tcp_24224_allow_cidr_blocks,
    data.aws_subnet.private_subnets.*.cidr_block,
  )

  security_group_id = aws_security_group.instance_security_group.id
}

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

resource "aws_iam_role" "instance_iam_role" {
  name               = "${var.environment}-${var.service_name}-instance-role"
  assume_role_policy = data.aws_iam_policy_document.assume_instance_role.json
}

resource "aws_iam_instance_profile" "iam_instance_profile" {
  name = "${var.environment}-${var.service_name}-instance-profile"
  role = aws_iam_role.instance_iam_role.name
}

# Allow IAM policy from file to configure case-specific access requirements, if enabled.
resource "aws_iam_policy" "iam_instance_policy" {
  count  = var.iam_policy_file_enabled ? 1 : 0
  name   = "${var.environment}-${var.service_name}-instance-policy"
  policy = file("policies/${var.service_name}.json")
}

# Attach additional policy to role, if enabled
resource "aws_iam_role_policy_attachment" "instance_iam_policy" {
  count      = var.iam_policy_file_enabled ? 1 : 0
  role       = aws_iam_role.instance_iam_role.name
  policy_arn = aws_iam_policy.iam_instance_policy[0].arn
}

# Attach additional existing managed IAM policies, if enabled
resource "aws_iam_role_policy_attachment" "existing_iam_policy_attachment" {
  count      = length(var.iam_managed_policy_attachments)
  role       = aws_iam_role.instance_iam_role.name
  policy_arn = var.iam_managed_policy_attachments[count.index]
}

# Launch configuration
resource "aws_launch_configuration" "launch_configuration" {
  name_prefix                 = "${var.environment}-${var.service_name}-launch-configuration"
  image_id                    = data.aws_ami.centos.id
  instance_type               = var.aws_instance_type
  associate_public_ip_address = false
  iam_instance_profile        = aws_iam_instance_profile.iam_instance_profile.name
  enable_monitoring           = true
  ebs_optimized               = true

  root_block_device {
    volume_type           = "gp2"
    volume_size           = var.root_volume_size
    delete_on_termination = true
    encrypted             = true
  }

  security_groups = [
    aws_security_group.instance_security_group.id,
  ]

  key_name  = var.ssh_keypair_name
  user_data = "${data.template_file.instance_user_data.rendered}\n${var.user_data}"

  lifecycle {
    create_before_destroy = true
  }
}

resource "aws_lb" "network_load_balancer" {
  name                             = "${var.environment}-${var.service_name}"
  internal                         = var.load_balancer_is_internal
  load_balancer_type               = "network"
  subnets                          = var.load_balancer_subnets
  enable_deletion_protection       = var.load_balancer_enable_deletion_protection
  enable_cross_zone_load_balancing = true

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

  tags = {
    environment  = var.environment
    service_name = var.service_name
    terraformed  = true
  }
}

# Target group for fluentd in_http service
resource "aws_lb_target_group" "http_8888_target_group" {
  count                = var.http_plugin_enabled ? 1 : 0
  name                 = "${var.environment}-${var.service_name}-http"
  port                 = 8888
  protocol             = "TCP"
  target_type          = "instance"
  deregistration_delay = var.target_deregistration_delay
  vpc_id               = var.vpc_id

  health_check {
    enabled             = var.target_group_health_check_enabled
    healthy_threshold   = var.health_check_healthy_threshold
    interval            = var.health_check_interval
    protocol            = "TCP"
    unhealthy_threshold = var.health_check_unhealthy_threshold
  }

  tags = {
    environment  = var.environment
    service_name = var.service_name
    terraformed  = true
  }

  depends_on = [aws_lb.network_load_balancer]
}

# Target group for fluentd in_forward service
resource "aws_lb_target_group" "tcp_24224_target_group" {
  name                 = "${var.environment}-${var.service_name}-tcp"
  port                 = 24224
  protocol             = "TCP"
  target_type          = "instance"
  deregistration_delay = var.target_deregistration_delay
  vpc_id               = var.vpc_id

  health_check {
    enabled             = var.target_group_health_check_enabled
    healthy_threshold   = var.health_check_healthy_threshold
    interval            = var.health_check_interval
    protocol            = "TCP"
    unhealthy_threshold = var.health_check_unhealthy_threshold
  }

  tags = {
    environment  = var.environment
    service_name = var.service_name
    terraformed  = true
  }

  depends_on = [aws_lb.network_load_balancer]
}

# Target group for fluentd UDP service
resource "aws_lb_target_group" "udp_target_group" {
  count                = var.udp_plugin_enabled ? 1 : 0
  name                 = "${var.environment}-${var.service_name}-udp"
  port                 = 5160
  protocol             = "UDP"
  target_type          = "instance"
  deregistration_delay = var.target_deregistration_delay
  vpc_id               = var.vpc_id

  # UDP health checks can only use TCP, so check the TCP port which is a reasonable indicator of UDP health
  health_check {
    enabled             = var.target_group_health_check_enabled
    healthy_threshold   = var.health_check_healthy_threshold
    interval            = var.health_check_interval
    port                = 24224
    protocol            = "TCP"
    unhealthy_threshold = var.health_check_unhealthy_threshold
  }


  tags = {
    environment  = var.environment
    service_name = var.service_name
    terraformed  = true
  }

  depends_on = [aws_lb.network_load_balancer]
}

resource "aws_lb_listener" "load_balancer_https_listener" {
  count             = var.http_plugin_enabled ? 1 : 0
  load_balancer_arn = aws_lb.network_load_balancer.arn
  port              = var.https_listener_port
  protocol          = "TLS"
  ssl_policy        = var.https_listener_ssl_policy
  certificate_arn   = data.aws_acm_certificate.acm_certificate.arn

  default_action {
    target_group_arn = aws_lb_target_group.http_8888_target_group[0].arn
    type             = "forward"
  }

  depends_on = [aws_lb_target_group.http_8888_target_group[0]]
}

resource "aws_lb_listener" "load_balancer_tcp_24224_listener" {
  load_balancer_arn = aws_lb.network_load_balancer.arn
  port              = 24224
  protocol          = "TCP"

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

  depends_on = [aws_lb_target_group.tcp_24224_target_group]
}

resource "aws_lb_listener" "load_balancer_udp_listener" {
  count             = var.udp_plugin_enabled ? 1 : 0
  load_balancer_arn = aws_lb.network_load_balancer.arn
  port              = 5160
  protocol          = "UDP"

  default_action {
    target_group_arn = aws_lb_target_group.udp_target_group[0].arn
    type             = "forward"
  }

  depends_on = [aws_lb_target_group.udp_target_group]
}

resource "aws_autoscaling_group" "autoscaling_group" {
  name                      = "${var.environment}-${var.service_name}-autoscaling-group"
  min_size                  = var.min_size
  max_size                  = var.max_size
  desired_capacity          = var.desired_capacity
  default_cooldown          = var.default_cooldown
  health_check_type         = "EC2"
  health_check_grace_period = var.health_check_grace_period
  wait_for_capacity_timeout = var.wait_for_capacity_timeout
  vpc_zone_identifier       = var.instance_subnet_ids
  launch_configuration      = aws_launch_configuration.launch_configuration.name
  enabled_metrics           = var.enabled_metrics
  target_group_arns         = local.target_group_arns

  tags = [
    {
      key                 = "Name"
      value               = "${var.environment}-${var.service_name}"
      propagate_at_launch = true
    },
    {
      key                 = "environment"
      value               = var.environment
      propagate_at_launch = true
    },
    {
      key                 = "service_name"
      value               = var.service_name
      propagate_at_launch = true
    },
  ]

  lifecycle {
    create_before_destroy = true
  }
}

resource "aws_autoscaling_policy" "scale_out_policy" {
  name                   = "${var.environment}-${var.service_name}-scale-out-policy"
  policy_type            = "SimpleScaling"
  scaling_adjustment     = 1
  cooldown               = 600
  adjustment_type        = "ChangeInCapacity"
  autoscaling_group_name = aws_autoscaling_group.autoscaling_group.name
}

resource "aws_autoscaling_policy" "scale_in_policy" {
  name                   = "${var.environment}-${var.service_name}-scale-in-policy"
  policy_type            = "SimpleScaling"
  scaling_adjustment     = -1
  cooldown               = 600
  adjustment_type        = "ChangeInCapacity"
  autoscaling_group_name = aws_autoscaling_group.autoscaling_group.name
}

resource "aws_cloudwatch_metric_alarm" "scale_out_alarm" {
  alarm_name          = "${var.environment}-${var.service_name}-scale-out-alarm"
  comparison_operator = "GreaterThanOrEqualToThreshold"
  evaluation_periods  = "5"
  metric_name         = "NetworkIn"
  namespace           = "AWS/EC2"
  period              = "60"
  statistic           = "Maximum"
  threshold           = "200000000"

  dimensions = {
    AutoScalingGroupName = aws_autoscaling_group.autoscaling_group.name
  }

  alarm_description = "This metric monitors Network In"
  alarm_actions     = [aws_autoscaling_policy.scale_out_policy.arn]
}

resource "aws_cloudwatch_metric_alarm" "scale_in_alarm" {
  alarm_name          = "${var.environment}-${var.service_name}-scale-in-alarm"
  comparison_operator = "GreaterThanOrEqualToThreshold"
  evaluation_periods  = "5"
  metric_name         = "NetworkIn"
  namespace           = "AWS/EC2"
  period              = "60"
  statistic           = "Maximum"
  threshold           = "100000000"

  dimensions = {
    AutoScalingGroupName = aws_autoscaling_group.autoscaling_group.name
  }

  alarm_description = "This metric monitors Network In"
  ok_actions        = [aws_autoscaling_policy.scale_in_policy.arn]
}

resource "aws_route53_record" "route53_record" {
  name    = "${var.environment}-${var.service_name}"
  zone_id = var.aws_route53_params["zone_id"]
  type    = var.aws_route53_params["type"]
  ttl     = var.aws_route53_params["ttl"]
  records = [aws_lb.network_load_balancer.dns_name]
}

resource "aws_route53_record" "route53_udp_record" {
  name    = "${var.environment}-${var.service_name}-udp"
  zone_id = var.aws_route53_params["zone_id"]
  type    = var.aws_route53_params["type"]
  ttl     = var.aws_route53_params["ttl"]
  records = [aws_lb.network_load_balancer.dns_name]
}
