# Get an existing vpc id
data "aws_vpc" "this" {
  id = var.vpc_id
}

# Get account information
data "aws_caller_identity" "current" {
}

data "aws_caller_identity" "dns" {
  provider = aws.dns
}

# Getting AMI id
data "aws_ami" "amazon-linux-2" {
  most_recent = true
  owners      = ["amazon"]

  filter {
    name   = "name"
    values = ["amzn2-ami-hvm-*-x86_64-gp2"]
  }
}

# Get a SG for SSH access from VPN
data "aws_security_group" "aws_sg" {
  filter {
    name = "group-name"

    values = [
      var.vpc_ssh_group,
    ]
  }
}

# Get the Route53 zone information based on environment
data "aws_route53_zone" "zone" {
  provider     = aws.dns
  zone_id      = local.route53_zone_id
  private_zone = false
}

data "aws_acm_certificate" "certificate" {
  domain   = "*.${data.aws_route53_zone.zone.name}"
  statuses = ["ISSUED"]
}

# IAM policy document for ec2 instance's role within ASG
data "aws_iam_policy_document" "instance-assume-role-policy" {
  statement {
    actions = ["sts:AssumeRole"]

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

# Target group for master instance
resource "aws_lb_target_group" "target_group" {
  name                 = var.aws_stack_name
  port                 = "8080"
  protocol             = "HTTP"
  target_type          = "instance"
  deregistration_delay = "60"
  vpc_id               = var.vpc_id

  health_check {
    enabled             = true
    healthy_threshold   = 3
    interval            = 10
    matcher             = "200-299"
    path                = "/login"
    port                = "traffic-port"
    protocol            = "HTTP"
    timeout             = 5
    unhealthy_threshold = 5
  }

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

  depends_on = [aws_lb.application_load_balancer]
}

resource "aws_autoscaling_attachment" "asg_attachment_bar" {
  autoscaling_group_name = aws_autoscaling_group.asg.id
  lb_target_group_arn    = aws_lb_target_group.target_group.arn
}

resource "aws_lb" "application_load_balancer" {
  name                       = var.aws_stack_name
  internal                   = true
  load_balancer_type         = "application"
  idle_timeout               = 60
  security_groups            = [aws_security_group.elb-sg.id]
  subnets                    = var.vpc_elb_subnet_ids
  enable_deletion_protection = false

  access_logs {
    bucket  = var.elb_logging_bucket
    prefix  = "${var.environment}-${var.aws_stack_name}"
    enabled = true
  }

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

resource "aws_lb_listener" "load_balancer_https_listener" {
  load_balancer_arn = aws_lb.application_load_balancer.arn
  port              = 443
  protocol          = "HTTPS"
  ssl_policy        = var.https_listener_ssl_policy
  certificate_arn   = data.aws_acm_certificate.certificate.arn

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

  depends_on = [aws_lb_target_group.target_group]
}

# Create a new volume for jenkins master
resource "aws_ebs_volume" "ebs" {
  availability_zone = var.az
  size              = var.aws_device_size
  type              = var.aws_ebs_volume_type
  encrypted         = true

  tags = {
    "Name"         = var.aws_stack_name
    "terraformed"  = "true"
    "environment"  = var.environment
    "service_name" = var.aws_stack_name
  }
}

# A security group for the ELB
# It should be accessible within corporate network
resource "aws_security_group" "elb-sg" {
  name        = "${var.aws_stack_name}-elb-sg"
  description = "SG for Load balancer"
  vpc_id      = var.vpc_id

  tags = {
    Name        = var.aws_stack_name
    environment = var.environment
    terraformed = true
  }
}

resource "aws_security_group_rule" "elb_access_ips_443" {
  type        = "ingress"
  from_port   = 443
  to_port     = 443
  protocol    = "tcp"
  cidr_blocks = var.aws_elb_access_ips

  security_group_id = aws_security_group.elb-sg.id
}

resource "aws_security_group_rule" "lb_source_sg_access_443" {
  for_each                 = toset(var.lb_source_sg_access)
  type                     = "ingress"
  from_port                = 443
  to_port                  = 443
  protocol                 = "tcp"
  source_security_group_id = each.value

  security_group_id = aws_security_group.elb-sg.id
}

resource "aws_security_group_rule" "aws_security_group_elb_sg_egress" {
  type      = "egress"
  from_port = 0
  to_port   = 0
  protocol  = -1
  cidr_blocks = [
    "0.0.0.0/0",
  ]

  security_group_id = aws_security_group.elb-sg.id
}

# Security group to access the instances from ELB
resource "aws_security_group" "asg-sg" {
  name        = "${var.aws_stack_name}-asg"
  description = "AWS Security group for ASG instances"
  vpc_id      = var.vpc_id

  # HTTP access from ELB security group
  ingress {
    from_port = 8080
    to_port   = 8080
    protocol  = "tcp"

    security_groups = [
      aws_security_group.elb-sg.id,
    ]
  }

  # outbound internet access
  egress {
    from_port = 0
    to_port   = 0
    protocol  = "-1"

    cidr_blocks = [
      "0.0.0.0/0",
    ]
  }

  tags = {
    Name        = var.aws_stack_name
    environment = var.environment
    terraformed = true
  }
}

# A security group for cluster instances
resource "aws_security_group" "cluster-sg" {
  name        = "${var.aws_stack_name}-cluster"
  description = "SG for communication between cluster instances"
  vpc_id      = var.vpc_id

  # SSH from within this SG
  ingress {
    from_port = 22
    to_port   = 22
    protocol  = "tcp"
    self      = true
  }

  # SSH for jenkins cli
  # Same port should be used in master chef recipe
  ingress {
    from_port = var.ssh_cli_port
    to_port   = var.ssh_cli_port
    protocol  = "tcp"
    self      = true
  }

  # Outbound internet access
  egress {
    from_port = 0
    to_port   = 0
    protocol  = "-1"

    cidr_blocks = [
      "0.0.0.0/0",
    ]
  }

  tags = {
    Name        = var.aws_stack_name
    environment = var.environment
    terraformed = true
  }
}

# IAM instance profile
resource "aws_iam_instance_profile" "iam-instance-profile" {
  name = var.aws_stack_name
  role = aws_iam_role.iam-role.name
}

# IAM role
resource "aws_iam_role" "iam-role" {
  name               = var.aws_stack_name
  assume_role_policy = data.aws_iam_policy_document.instance-assume-role-policy.json
}

# Policy document for IAM policy
data "aws_iam_policy_document" "iam_policy_document" {
  statement {
    actions = [
      "ec2:AttachVolume",
      "ec2:DetachVolume",
    ]

    resources = [
      "arn:aws:ec2:${var.aws_region}:${data.aws_caller_identity.current.account_id}:instance/*",
      "arn:aws:ec2:${var.aws_region}:${data.aws_caller_identity.current.account_id}:volume/*",
    ]

    condition {
      test     = "StringEquals"
      values   = [var.aws_stack_name]
      variable = "ec2:ResourceTag/Name"
    }
  }

  statement {
    actions = [
      "ec2:DescribeVolumes",
    ]

    resources = [
      "*",
    ]
  }
}

resource "aws_iam_policy" "iam_policy" {
  name   = "${var.aws_stack_name}-ebs"
  policy = data.aws_iam_policy_document.iam_policy_document.json
}

resource "aws_iam_policy_attachment" "jenkins_stack_attachement" {
  name       = "jenkins-stack-ebs-attachment"
  roles      = [aws_iam_role.iam-role.id]
  policy_arn = aws_iam_policy.iam_policy.arn
}

# Launch template
resource "aws_launch_template" "jenkins_aws_main_launch_template" {
  name_prefix   = "${var.aws_stack_name}-lt-"
  image_id      = data.aws_ami.amazon-linux-2.id
  instance_type = var.aws_instance_type

  iam_instance_profile {
    name = aws_iam_instance_profile.iam-instance-profile.name
  }

  monitoring {
    enabled = true
  }

  vpc_security_group_ids = [
    aws_security_group.asg-sg.id,
    data.aws_security_group.aws_sg.id,
    aws_security_group.cluster-sg.id,
  ]

  key_name  = var.aws_key_name
  user_data = local.combined_user_data

  lifecycle {
    create_before_destroy = true
  }

  tag_specifications {
    resource_type = "volume"
    tags = {
      Name = "${var.aws_stack_name}-root-volume"
    }
  }
}

# Auto scaling group
resource "aws_autoscaling_group" "asg" {
  name                      = var.aws_stack_name
  max_size                  = 1
  min_size                  = 1
  desired_capacity          = 1
  health_check_type         = "EC2"
  health_check_grace_period = 900

  # Instances must be in same AZ as EBS volume
  vpc_zone_identifier = var.vpc_private_subnet_ids
  suspended_processes = var.asg_suspended_processes

  launch_template {
    id      = aws_launch_template.jenkins_aws_main_launch_template.id
    version = "$Latest"
  }

  tag {
    key                 = "Name"
    value               = var.aws_stack_name
    propagate_at_launch = true
  }

  lifecycle {
    create_before_destroy = true
    ignore_changes        = [load_balancers, target_group_arns]
  }
}

# Create DNS record based on environment
resource "aws_route53_record" "dns" {
  provider = aws.dns

  zone_id = local.route53_zone_id
  name    = var.aws_stack_name
  type    = "CNAME"
  records = [aws_lb.application_load_balancer.dns_name]
  ttl     = var.route53_record_ttl
}

# VolumeId
output "ebs_volume_id" {
  value = aws_ebs_volume.ebs.id
}

# Stack name variable to use in other terraform modules
output "aws_stack_name_output" {
  value = var.aws_stack_name
}

# ASG arn variable to use in other terraform modules
output "aws_asg_name_output" {
  value = aws_autoscaling_group.asg.name
}

# Route53 record output
output "aws_route53_record" {
  description = "FQDN of the Jenkins service DNS record"
  value       = aws_route53_record.dns.fqdn
}

# Cluster security group
output "aws_cluster_sg" {
  value = aws_security_group.cluster-sg.id
}

# IAM role variable to use in other terraform modules
output "aws_iam_role_id_output" {
  value = aws_iam_role.iam-role.id
}

output "jenkins_alb_dns_name" {
  value = aws_lb.application_load_balancer.dns_name
}

output "jenkins_alb_arn" {
  value = aws_lb.application_load_balancer.arn
}

output "elb_security_group" {
  value = aws_security_group.elb-sg.id
}
