terraform {
  required_providers {
    awsutils = {
      source = "cloudposse/awsutils"
    }
  }
}

provider "awscc" {
  region = data.aws_region.current.region
  alias  = "awscccurrent"
}

resource "aws_account_alternate_contact" "operations" {
  count                  = trimspace(var.operations_contact.email) != "" ? 1 : 0
  alternate_contact_type = var.operations_contact.type
  name                   = var.operations_contact.name
  title                  = var.operations_contact.title
  email_address          = var.operations_contact.email
  phone_number           = var.operations_contact.phone
}

resource "aws_account_alternate_contact" "security" {
  count                  = trimspace(var.security_contact.email) != "" ? 1 : 0
  alternate_contact_type = var.security_contact.type
  name                   = var.security_contact.name
  title                  = var.security_contact.title
  email_address          = var.security_contact.email
  phone_number           = var.security_contact.phone
}

resource "aws_account_alternate_contact" "billing" {
  count                  = trimspace(var.billing_contact.email) != "" ? 1 : 0
  alternate_contact_type = var.billing_contact.type
  name                   = var.billing_contact.name
  title                  = var.billing_contact.title
  email_address          = var.billing_contact.email
  phone_number           = var.billing_contact.phone
}

resource "aws_iam_account_password_policy" "password_policy" {
  allow_users_to_change_password = var.allow_users_to_change_password
  hard_expiry                    = var.hard_expiry
  max_password_age               = var.max_password_age
  minimum_password_length        = var.minimum_password_length
  password_reuse_prevention      = var.password_reuse_prevention
  require_lowercase_characters   = var.require_lowercase_characters
  require_numbers                = var.require_numbers
  require_uppercase_characters   = var.require_uppercase_characters
  require_symbols                = var.require_symbols
}

resource "aws_s3_account_public_access_block" "s3_public_block" {
  block_public_acls       = true
  block_public_policy     = true
  restrict_public_buckets = true
  ignore_public_acls      = true
}

# Enforce EBS encryption
resource "aws_ebs_encryption_by_default" "encryption" {
  enabled = true
}

# Prevent public AMIs
resource "aws_ec2_image_block_public_access" "ami" {
  state = "block-new-sharing"
}

# Prevent public Snapshot
resource "awscc_ec2_snapshot_block_public_access" "snapshot" {
  provider = awscc.awscccurrent
  state    = "block-all-sharing"
}

resource "aws_budgets_budget" "budgets" {
  name         = var.budget_name
  budget_type  = var.budget_type
  limit_amount = var.limit_amount
  limit_unit   = var.limit_unit
  time_unit    = var.time_unit

  cost_types {
    include_credit             = var.include_credit
    include_discount           = var.include_discount
    include_other_subscription = var.include_other_subscription
    include_recurring          = var.include_recurring
    include_refund             = var.include_refund
    include_subscription       = var.include_subscription
    include_support            = var.include_support
    include_tax                = var.include_tax
    include_upfront            = var.include_upfront
    use_blended                = var.use_blended
  }

  notification {
    comparison_operator        = "GREATER_THAN"
    threshold                  = 50
    threshold_type             = "PERCENTAGE"
    notification_type          = "ACTUAL"
    subscriber_email_addresses = var.subscriber_email_addresses
  }

  notification {
    comparison_operator        = "GREATER_THAN"
    threshold                  = 100
    threshold_type             = "PERCENTAGE"
    notification_type          = "ACTUAL"
    subscriber_email_addresses = var.subscriber_email_addresses
  }

  notification {
    comparison_operator        = "GREATER_THAN"
    threshold                  = 150
    threshold_type             = "PERCENTAGE"
    notification_type          = "ACTUAL"
    subscriber_email_addresses = var.escalations_email_addresses
  }

  notification {
    comparison_operator        = "GREATER_THAN"
    threshold                  = 50
    threshold_type             = "PERCENTAGE"
    notification_type          = "FORECASTED"
    subscriber_email_addresses = var.subscriber_email_addresses
  }

  notification {
    comparison_operator        = "GREATER_THAN"
    threshold                  = 100
    threshold_type             = "PERCENTAGE"
    notification_type          = "FORECASTED"
    subscriber_email_addresses = var.subscriber_email_addresses
  }

  notification {
    comparison_operator        = "GREATER_THAN"
    threshold                  = 150
    threshold_type             = "PERCENTAGE"
    notification_type          = "FORECASTED"
    subscriber_email_addresses = var.monitor_email_addresses
  }
}

# Technical alerts
resource "aws_kms_key" "sns_key" {
  description             = "sns-topic-key"
  deletion_window_in_days = 30
  policy                  = data.aws_iam_policy_document.sns_policy.json
  enable_key_rotation     = true
}

resource "aws_sns_topic" "technical_alerts_topic" {
  name              = "${var.name}-technical-alert"
  kms_master_key_id = aws_kms_key.sns_key.id

  tags = merge(var.service_tags, {
    Name = "${var.name}-technical-alert"
  })
}


resource "aws_sns_topic_subscription" "technical_alerts_email_target" {
  topic_arn = aws_sns_topic.technical_alerts_topic.arn
  protocol  = "email"
  endpoint  = var.sns_subscriber_email
}

resource "aws_kms_alias" "sns_key_alias" {
  name          = "alias/${var.name}-sns-key"
  target_key_id = aws_kms_key.sns_key.id
}

# Backup plans
resource "aws_backup_vault" "backup_vault" {
  name        = "${var.name}-backup-vault"
  kms_key_arn = data.aws_kms_key.backup.arn
  tags = {
    Name = "${var.name}-backup-vault"
  }
}

resource "aws_backup_plan" "daily_backup_plan" {
  name = "${var.name}-daily-backup-plan"

  rule {
    rule_name                = "daily_backup_rule"
    target_vault_name        = aws_backup_vault.backup_vault.name
    schedule                 = var.daily_backup_plan
    start_window             = var.backup_start_window
    completion_window        = var.backup_completion_window
    enable_continuous_backup = false

    recovery_point_tags = {
      Name = "${var.name}-backups"
    }

    lifecycle {
      cold_storage_after = var.daily_backup_plan_move_to_cold_storage
      delete_after       = var.daily_backup_plan_delete_after
    }
  }
}

resource "aws_backup_selection" "daily_backups" {
  iam_role_arn = aws_iam_role.backup.arn
  name         = "${var.name}-backups"
  plan_id      = aws_backup_plan.daily_backup_plan.id

  selection_tag {
    type  = "STRINGEQUALS"
    key   = "env"
    value = "backup"
  }
}

resource "aws_backup_plan" "weekly_backup_plan" {
  name = "${var.name}-weekly-backup-plan"

  rule {
    rule_name                = "weekly_backup_rule"
    target_vault_name        = aws_backup_vault.backup_vault.name
    schedule                 = var.weekly_backup_plan
    start_window             = var.backup_start_window
    completion_window        = var.backup_completion_window
    enable_continuous_backup = false

    recovery_point_tags = {
      Name = "${var.name}-backups"
    }

    lifecycle {
      cold_storage_after = var.weekly_backup_plan_move_to_cold_storage
      delete_after       = var.weekly_backup_plan_delete_after
    }
  }
}

resource "aws_backup_selection" "weekly_backups" {
  iam_role_arn = aws_iam_role.backup.arn
  name         = "${var.name}-backups"
  plan_id      = aws_backup_plan.weekly_backup_plan.id

  selection_tag {
    type  = "STRINGEQUALS"
    key   = "env"
    value = "backup"
  }
}

resource "aws_backup_plan" "monthly_backup_plan" {
  name = "${var.name}-monthly-backup-plan"

  rule {
    rule_name                = "monthly_backup_rule"
    target_vault_name        = aws_backup_vault.backup_vault.name
    schedule                 = var.monthly_backup_plan
    start_window             = var.backup_start_window
    completion_window        = var.backup_completion_window
    enable_continuous_backup = false

    recovery_point_tags = {
      Name = "${var.name}-backups"
    }

    lifecycle {
      cold_storage_after = var.monthly_backup_plan_move_to_cold_storage
      delete_after       = var.monthly_backup_plan_delete_after
    }
  }
}

resource "aws_backup_selection" "monthly_backups" {
  iam_role_arn = aws_iam_role.backup.arn
  name         = "${var.name}-backups"
  plan_id      = aws_backup_plan.monthly_backup_plan.id

  selection_tag {
    type  = "STRINGEQUALS"
    key   = "env"
    value = "backup"
  }
}

resource "aws_iam_role" "backup" {
  name               = "${var.name}-aws-backup-role"
  assume_role_policy = data.aws_iam_policy_document.backups.json
}

resource "aws_iam_role_policy_attachment" "aws_backup" {
  policy_arn = "arn:aws:iam::aws:policy/service-role/AWSBackupServiceRolePolicyForBackup"
  role       = aws_iam_role.backup.name
}

# Cloudwatch alerts
resource "aws_cloudwatch_log_metric_filter" "root_usage_alarm" {
  name           = "RootUsage"
  pattern        = "{ $.userIdentity.type = \"Root\" && $.userIdentity.invokedBy NOT EXISTS && $.eventType != \"AwsServiceEvent\" }"
  log_group_name = var.cloudwatch_alarm_log_group

  metric_transformation {
    name      = "CloudTrailChanges"
    namespace = var.alarm_namespace
    value     = "1"
  }
}

resource "aws_cloudwatch_metric_alarm" "root_usage_alarm" {
  alarm_name                = "RootUsageAlarm"
  comparison_operator       = "GreaterThanOrEqualToThreshold"
  evaluation_periods        = "1"
  metric_name               = aws_cloudwatch_log_metric_filter.root_usage_alarm.id
  namespace                 = var.alarm_namespace
  period                    = "300"
  statistic                 = "Sum"
  threshold                 = "3"
  alarm_description         = "Monitoring for root account logins will provide visibility into the use of a fully privileged account and an opportunity to reduce the use of it."
  alarm_actions             = [aws_sns_topic.technical_alerts_topic.arn]
  ok_actions                = [aws_sns_topic.technical_alerts_topic.arn]
  treat_missing_data        = "missing"
  insufficient_data_actions = []
}

resource "aws_cloudwatch_log_metric_filter" "cloudtrail_changes" {
  name           = "CloudTrailChanges"
  pattern        = "{ ($.eventName = CreateTrail) || ($.eventName = UpdateTrail) || ($.eventName = DeleteTrail) || ($.eventName = StartLogging) || ($.eventName = StopLogging) }"
  log_group_name = var.cloudwatch_alarm_log_group

  metric_transformation {
    name      = "CloudTrailChanges"
    namespace = var.alarm_namespace
    value     = "1"
  }
}

resource "aws_cloudwatch_metric_alarm" "cloudtrail_changes" {
  alarm_name                = "CloudTrailChangesAlarm"
  comparison_operator       = "GreaterThanOrEqualToThreshold"
  evaluation_periods        = "1"
  metric_name               = aws_cloudwatch_log_metric_filter.cloudtrail_changes.id
  namespace                 = var.alarm_namespace
  period                    = "300"
  statistic                 = "Sum"
  threshold                 = "3"
  alarm_description         = "Monitoring changes to CloudTrail's configuration will help ensure sustained visibility to activities performed in the AWS account."
  alarm_actions             = [aws_sns_topic.technical_alerts_topic.arn]
  ok_actions                = [aws_sns_topic.technical_alerts_topic.arn]
  treat_missing_data        = "missing"
  insufficient_data_actions = []

}


resource "aws_cloudwatch_log_metric_filter" "login_failure" {
  name           = "ConsoleSigninFailures"
  pattern        = "{ ($.eventName = ConsoleLogin) && ($.errorMessage = \"Failed authentication\") }"
  log_group_name = var.cloudwatch_alarm_log_group

  metric_transformation {
    name      = "ConsoleSigninFailures"
    namespace = var.alarm_namespace
    value     = "1"
  }
}

resource "aws_cloudwatch_metric_alarm" "login_failure" {
  alarm_name                = "ConsoleSigninFailuresAlarm"
  comparison_operator       = "GreaterThanOrEqualToThreshold"
  evaluation_periods        = "1"
  metric_name               = aws_cloudwatch_log_metric_filter.login_failure.id
  namespace                 = var.alarm_namespace
  period                    = "300"
  statistic                 = "Sum"
  threshold                 = "3"
  alarm_description         = "Monitoring failed console logins may decrease lead time to detect an attempt to brute force a credential, which may provide an indicator, such as source IP, that can be used in other event correlation."
  alarm_actions             = [aws_sns_topic.technical_alerts_topic.arn]
  ok_actions                = [aws_sns_topic.technical_alerts_topic.arn]
  treat_missing_data        = "missing"
  insufficient_data_actions = []
}
