locals {
  gitlab_oidc_condition_prefix = trimprefix(var.gitlab_oidc_issuer, "https://")
  flow_log_group_arn           = "arn:aws:logs:${var.aws_region}:${data.aws_caller_identity.current.account_id}:log-group:/aws/vpc/${var.name}-${var.environment}/flow-logs"
  flow_log_group_lifecycle_arn = "${local.flow_log_group_arn}:*"
  workload_kms_alias_arn       = "arn:aws:kms:${var.aws_region}:${data.aws_caller_identity.current.account_id}:alias/${var.name}-${var.environment}-workload"
  workload_secret_arn_pattern  = "arn:aws:secretsmanager:${var.aws_region}:${data.aws_caller_identity.current.account_id}:secret:${var.name}/${var.environment}/workload-metadata-*"
  monthly_budget_arn           = "arn:aws:budgets::${data.aws_caller_identity.current.account_id}:budget/${var.name}-${var.environment}-account-wide-monthly-alert"
}

resource "aws_kms_key" "workload" {
  description             = "Encrypts metadata managed by the foundation security boundary."
  deletion_window_in_days = 30
  enable_key_rotation     = true
  policy = jsonencode({
    Version = "2012-10-17"
    Statement = [{
      Sid       = "EnableAccountIamPolicies"
      Effect    = "Allow"
      Principal = { AWS = "arn:aws:iam::${data.aws_caller_identity.current.account_id}:root" }
      Action    = "kms:*"
      Resource  = "*"
    }]
  })

  lifecycle {
    prevent_destroy = true
  }
}

resource "aws_kms_alias" "workload" {
  name          = "alias/${var.name}-${var.environment}-workload"
  target_key_id = aws_kms_key.workload.key_id
}

resource "aws_secretsmanager_secret" "workload_metadata" {
  name                    = "${var.name}/${var.environment}/workload-metadata"
  description             = "Metadata container only; secret values are supplied outside Terraform."
  kms_key_id              = aws_kms_key.workload.arn
  recovery_window_in_days = var.secret_recovery_days
}

resource "aws_iam_role" "workload" {
  name = "${var.name}-${var.environment}-workload"
  assume_role_policy = jsonencode({
    Version = "2012-10-17"
    Statement = [{
      Effect    = "Allow"
      Action    = "sts:AssumeRole"
      Principal = { Service = "ec2.amazonaws.com" }
    }]
  })
}

resource "aws_iam_role_policy" "workload_metadata" {
  name = "${var.name}-${var.environment}-workload-metadata"
  role = aws_iam_role.workload.id
  policy = jsonencode({
    Version = "2012-10-17"
    Statement = [
      {
        Effect   = "Allow"
        Action   = ["secretsmanager:DescribeSecret", "secretsmanager:GetSecretValue"]
        Resource = aws_secretsmanager_secret.workload_metadata.arn
      },
      {
        Effect   = "Allow"
        Action   = ["kms:Decrypt", "kms:DescribeKey"]
        Resource = aws_kms_key.workload.arn
        Condition = {
          StringEquals = { "kms:ViaService" = "secretsmanager.${var.aws_region}.amazonaws.com" }
        }
      },
    ]
  })
}

resource "aws_iam_role" "ci" {
  count = var.gitlab_oidc_provider_arn == null ? 0 : 1

  name                 = "${var.name}-${var.environment}-gitlab-ci"
  permissions_boundary = var.ci_permissions_boundary_arn
  assume_role_policy = jsonencode({
    Version = "2012-10-17"
    Statement = [{
      Effect    = "Allow"
      Action    = "sts:AssumeRoleWithWebIdentity"
      Principal = { Federated = var.gitlab_oidc_provider_arn }
      Condition = {
        StringEquals = {
          "${local.gitlab_oidc_condition_prefix}:aud" = "sts.amazonaws.com"
          "${local.gitlab_oidc_condition_prefix}:sub" = "project_path:${var.gitlab_project_path}:ref_type:branch:ref:${var.gitlab_protected_ref}"
        }
      }
    }]
  })
}

resource "aws_iam_role_policy" "ci_foundation" {
  count = var.gitlab_oidc_provider_arn == null ? 0 : 1

  name = "${var.name}-${var.environment}-gitlab-ci-foundation"
  role = aws_iam_role.ci[0].id
  policy = jsonencode({
    Version = "2012-10-17"
    Statement = [
      {
        Sid      = "StateObjectAndLock"
        Effect   = "Allow"
        Action   = ["s3:GetObject", "s3:PutObject", "s3:DeleteObject"]
        Resource = ["${var.ci_state_bucket_arn}/${var.ci_state_key}", "${var.ci_state_bucket_arn}/${var.ci_state_key}.tflock"]
      },
      {
        Sid      = "StateBucket"
        Effect   = "Allow"
        Action   = ["s3:ListBucket", "s3:GetBucketVersioning"]
        Resource = var.ci_state_bucket_arn
      },
      {
        Sid      = "StateEncryption"
        Effect   = "Allow"
        Action   = ["kms:Decrypt", "kms:Encrypt", "kms:GenerateDataKey", "kms:DescribeKey"]
        Resource = var.ci_state_kms_key_arn
      },
      {
        Sid    = "RegionalEC2Lifecycle"
        Effect = "Allow"
        Action = [
          "ec2:AllocateAddress",
          "ec2:AssociateRouteTable",
          "ec2:AttachInternetGateway",
          "ec2:AuthorizeSecurityGroupEgress",
          "ec2:AuthorizeSecurityGroupIngress",
          "ec2:CreateFlowLogs",
          "ec2:CreateInternetGateway",
          "ec2:CreateNatGateway",
          "ec2:CreateRoute",
          "ec2:CreateRouteTable",
          "ec2:CreateSecurityGroup",
          "ec2:CreateSubnet",
          "ec2:CreateTags",
          "ec2:CreateVpc",
          "ec2:CreateVpcEndpoint",
          "ec2:DeleteFlowLogs",
          "ec2:DeleteInternetGateway",
          "ec2:DeleteNatGateway",
          "ec2:DeleteRoute",
          "ec2:DeleteRouteTable",
          "ec2:DeleteSecurityGroup",
          "ec2:DeleteSubnet",
          "ec2:DeleteTags",
          "ec2:DeleteVpc",
          "ec2:DeleteVpcEndpoints",
          "ec2:DescribeAddresses",
          "ec2:DescribeAvailabilityZones",
          "ec2:DescribeFlowLogs",
          "ec2:DescribeInternetGateways",
          "ec2:DescribeNatGateways",
          "ec2:DescribeRouteTables",
          "ec2:DescribeSecurityGroups",
          "ec2:DescribeSubnets",
          "ec2:DescribeVpcAttribute",
          "ec2:DescribeVpcEndpoints",
          "ec2:DescribeVpcs",
          "ec2:DetachInternetGateway",
          "ec2:DisassociateRouteTable",
          "ec2:ModifySubnetAttribute",
          "ec2:ModifyVpcAttribute",
          "ec2:ModifyVpcEndpoint",
          "ec2:ReleaseAddress",
          "ec2:ReplaceRoute",
          "ec2:ReplaceRouteTableAssociation",
          "ec2:RevokeSecurityGroupEgress",
          "ec2:RevokeSecurityGroupIngress",
          "ec2:UpdateSecurityGroupRuleDescriptionsEgress",
          "ec2:UpdateSecurityGroupRuleDescriptionsIngress",
        ]
        Resource  = "*"
        Condition = { StringEquals = { "aws:RequestedRegion" = var.aws_region } }
      },
      {
        Sid       = "DiscoverFlowLogGroups"
        Effect    = "Allow"
        Action    = ["logs:DescribeLogGroups"]
        Resource  = "*"
        Condition = { StringEquals = { "aws:RequestedRegion" = var.aws_region } }
      },
      {
        Sid       = "ManagedFlowLogGroupLifecycle"
        Effect    = "Allow"
        Action    = ["logs:CreateLogGroup", "logs:DeleteLogGroup", "logs:PutRetentionPolicy", "logs:DeleteRetentionPolicy"]
        Resource  = local.flow_log_group_lifecycle_arn
        Condition = { StringEquals = { "aws:RequestedRegion" = var.aws_region } }
      },
      {
        Sid       = "ManagedFlowLogGroupTags"
        Effect    = "Allow"
        Action    = ["logs:ListTagsForResource", "logs:TagResource", "logs:UntagResource"]
        Resource  = local.flow_log_group_arn
        Condition = { StringEquals = { "aws:RequestedRegion" = var.aws_region } }
      },
      {
        Sid       = "CreateManagedKMSKey"
        Effect    = "Allow"
        Action    = ["kms:CreateKey"]
        Resource  = "*"
        Condition = { StringEquals = { "aws:RequestedRegion" = var.aws_region } }
      },
      {
        Sid       = "DiscoverKMSAliases"
        Effect    = "Allow"
        Action    = ["kms:ListAliases"]
        Resource  = "*"
        Condition = { StringEquals = { "aws:RequestedRegion" = var.aws_region } }
      },
      {
        Sid       = "ManagedWorkloadKMSKey"
        Effect    = "Allow"
        Action    = ["kms:DescribeKey", "kms:GetKeyPolicy", "kms:GetKeyRotationStatus", "kms:ListResourceTags", "kms:EnableKeyRotation", "kms:PutKeyPolicy", "kms:UpdateKeyDescription", "kms:TagResource", "kms:UntagResource"]
        Resource  = aws_kms_key.workload.arn
        Condition = { StringEquals = { "aws:RequestedRegion" = var.aws_region } }
      },
      {
        Sid      = "UseManagedWorkloadKMSKeyForSecretsManager"
        Effect   = "Allow"
        Action   = ["kms:Decrypt", "kms:GenerateDataKey"]
        Resource = aws_kms_key.workload.arn
        Condition = {
          StringEquals = { "kms:ViaService" = "secretsmanager.${var.aws_region}.amazonaws.com" }
        }
      },
      {
        Sid       = "ManagedWorkloadKMSAlias"
        Effect    = "Allow"
        Action    = ["kms:CreateAlias", "kms:UpdateAlias", "kms:DeleteAlias"]
        Resource  = [local.workload_kms_alias_arn, aws_kms_key.workload.arn]
        Condition = { StringEquals = { "aws:RequestedRegion" = var.aws_region } }
      },
      {
        Sid       = "ManagedWorkloadSecret"
        Effect    = "Allow"
        Action    = ["secretsmanager:CreateSecret", "secretsmanager:DeleteSecret", "secretsmanager:DescribeSecret", "secretsmanager:GetResourcePolicy", "secretsmanager:UpdateSecret", "secretsmanager:TagResource", "secretsmanager:UntagResource"]
        Resource  = local.workload_secret_arn_pattern
        Condition = { StringEquals = { "aws:RequestedRegion" = var.aws_region } }
      },
      {
        Sid      = "ManageBootstrapOwnedCIRole"
        Effect   = "Allow"
        Action   = ["iam:GetRole", "iam:PutRolePolicy", "iam:DeleteRolePolicy", "iam:TagRole", "iam:UntagRole", "iam:UpdateAssumeRolePolicy", "iam:GetRolePolicy", "iam:ListRolePolicies", "iam:ListAttachedRolePolicies", "iam:ListInstanceProfilesForRole"]
        Resource = aws_iam_role.ci[0].arn
      },
      {
        Sid      = "ManagedRoleLifecycle"
        Effect   = "Allow"
        Action   = ["iam:GetRole", "iam:DeleteRole", "iam:PutRolePolicy", "iam:DeleteRolePolicy", "iam:TagRole", "iam:UntagRole", "iam:UpdateAssumeRolePolicy", "iam:GetRolePolicy", "iam:ListRolePolicies", "iam:ListAttachedRolePolicies", "iam:ListInstanceProfilesForRole"]
        Resource = [aws_iam_role.workload.arn, "arn:aws:iam::${data.aws_caller_identity.current.account_id}:role/${var.name}-${var.environment}-flow-logs"]
      },
      {
        Sid      = "CreateManagedRoles"
        Effect   = "Allow"
        Action   = ["iam:CreateRole"]
        Resource = [aws_iam_role.workload.arn, "arn:aws:iam::${data.aws_caller_identity.current.account_id}:role/${var.name}-${var.environment}-flow-logs"]
      },
      {
        Sid       = "PassFlowLogsRole"
        Effect    = "Allow"
        Action    = ["iam:PassRole"]
        Resource  = "arn:aws:iam::${data.aws_caller_identity.current.account_id}:role/${var.name}-${var.environment}-flow-logs"
        Condition = { StringEquals = { "iam:PassedToService" = "vpc-flow-logs.amazonaws.com" } }
      },
      {
        Sid      = "ManagedMonthlyBudget"
        Effect   = "Allow"
        Action   = ["budgets:ModifyBudget", "budgets:ViewBudget", "budgets:ListTagsForResource", "budgets:TagResource", "budgets:UntagResource"]
        Resource = local.monthly_budget_arn
      },
      {
        Sid      = "BudgetBillingAccess"
        Effect   = "Allow"
        Action   = ["aws-portal:ModifyBilling", "aws-portal:ViewBilling"]
        Resource = "*"
      },
    ]
  })
}

resource "aws_security_group" "workload" {
  name        = "${var.name}-${var.environment}-workload"
  description = "Workload boundary: no ingress and HTTPS egress only."
  vpc_id      = module.network.vpc_id

  egress {
    description = "HTTPS for approved external and AWS service endpoints."
    from_port   = 443
    to_port     = 443
    protocol    = "tcp"
    cidr_blocks = ["0.0.0.0/0"]
  }
}

resource "aws_budgets_budget" "monthly" {
  name         = "${var.name}-${var.environment}-account-wide-monthly-alert"
  budget_type  = "COST"
  limit_amount = tostring(var.budget_limit_usd)
  limit_unit   = "USD"
  time_unit    = "MONTHLY"

  notification {
    comparison_operator        = "GREATER_THAN"
    threshold                  = 80
    threshold_type             = "PERCENTAGE"
    notification_type          = "ACTUAL"
    subscriber_email_addresses = [var.budget_alert_email]
  }
}
