# IAM Policy to restrict direct access to break-glass workflow resources.
resource "aws_iam_policy" "break_glass_restrict_direct_access_policy" {
  name   = local.break_glass_restrict_direct_access_policy_name
  policy = data.aws_iam_policy_document.break_glass_restrict_direct_access_policy_document.json
  tags   = local.tags
}

data "aws_iam_policy_document" "break_glass_restrict_direct_access_policy_document" {
  statement {
    effect = "Deny"
    actions = [
      "lambda:InvokeAsync",
      "lambda:InvokeFunction",
    ]
    resources = [
      module.elevate_permissions_lambda.lambda_arn,
      "${module.elevate_permissions_lambda.lambda_arn}:*"
    ]
  }
}

# IAM Role that will be assumed to get elevated permissions.
resource "aws_iam_role" "break_glass_role" {
  name                 = local.break_glass_role_name
  assume_role_policy   = data.aws_iam_policy_document.break_glass_assume_role_policy.json
  max_session_duration = var.role_session_duration
  tags                 = local.tags
}

data "aws_iam_policy_document" "break_glass_assume_role_policy" {
  statement {
    actions = ["sts:AssumeRole"]
    principals {
      type        = "AWS"
      identifiers = [data.aws_caller_identity.current.account_id]
    }
  }
}

# Attach existing IAM Policies to the role.
data "aws_iam_policy" "break_glass_role_existing_policies" {
  for_each = toset(var.role_policies)
  name     = each.value
}

resource "aws_iam_role_policy_attachment" "break_glass_role_existing_policy_attachments" {
  for_each = toset(var.role_policies)

  role       = aws_iam_role.break_glass_role.name
  policy_arn = data.aws_iam_policy.break_glass_role_existing_policies[each.value].arn
}

# Attach elevated permissions IAM Policy to the role.
resource "aws_iam_policy" "break_glass_elevate_permissions_policy" {
  name   = local.break_glass_elevate_permissions_policy_name
  policy = data.aws_iam_policy_document.break_glass_elevate_permissions_policy_document.json
  tags   = local.tags
}

data "aws_iam_policy_document" "break_glass_elevate_permissions_policy_document" {
  # checkov:skip=CKV_AWS_107:For better or worse, a widely permissive policy is the goal.
  # checkov:skip=CKV_AWS_108:Same as above.
  # checkov:skip=CKV_AWS_109:Same as above.
  # checkov:skip=CKV_AWS_110:Same as above.
  # checkov:skip=CKV_AWS_111:Same as above.

  statement {
    sid    = "AllowServiceReadOnlyAccess"
    effect = "Allow"
    actions = [
      "ec2:Describe*",
      "ec2:Get*",
      "ec2:List*",
      "ec2:Search*",
      "iam:Get*",
      "iam:List*",
    ]
    resources = ["*"]
  }

  statement {
    sid    = "AllowServiceFullAccess"
    effect = "Allow"
    actions = [
      "acm:*",
      "airflow:*",
      "apigateway:*",
      "application-autoscaling:*",
      "athena:*",
      "autoscaling:*",
      "batch:*",
      "cloudformation:*",
      "cloudfront:*",
      "cloudsearch:*",
      "cloudwatch:*",
      "dlm:*",
      "dynamodb:*",
      "ebs:*",
      "ecr:*",
      "ecs:*",
      "elasticache:*",
      "elasticbeanstalk:*",
      "elasticfilesystem:*",
      "elasticloadbalancing:*",
      "elasticmapreduce:*",
      "elastictranscoder:*",
      "es:*",
      "events:*",
      "glue:*",
      "kafka:*",
      "kafka-cluster:*",
      "kafkaconnect:*",
      "kendra:*",
      "kinesis:*",
      "kinesisanalytics:*",
      "kms:*",
      "lakeformation:*",
      "lambda:*",
      "logs:*",
      "mediaconvert:*",
      "rds:*",
      "route53:*",
      "s3:*",
      "secretsmanager:*",
      "sns:*",
      "sqs:*",
      "states:*",
      "swf:*",
      "wafv2:*",
      "xray:*",
    ]
    resources = ["*"]
  }

  statement {
    sid    = "AllowAttachingIAMPolicies"
    effect = "Allow"
    actions = [
      "iam:AttachRolePolicy",
      "iam:AttachUserPolicy",
    ]
    resources = [
      "arn:aws:iam::${data.aws_caller_identity.current.account_id}:role/${var.environment}-*",
      "arn:aws:iam::${data.aws_caller_identity.current.account_id}:role/lambda-${var.environment}-*",
      "arn:aws:iam::${data.aws_caller_identity.current.account_id}:role/StepFunctions-${var.environment}-*",
      "arn:aws:iam::${data.aws_caller_identity.current.account_id}:user/${var.environment}-*",
    ]
  }

  statement {
    sid     = "AllowPassingIAMRoles"
    effect  = "Allow"
    actions = ["iam:PassRole"]
    resources = [
      "arn:aws:iam::${data.aws_caller_identity.current.account_id}:role/aws-service-role/ecs.application-autoscaling.amazonaws.com/AWSServiceRoleForApplicationAutoScaling_ECSService",
      "arn:aws:iam::${data.aws_caller_identity.current.account_id}:role/lambda-${var.environment}-*",
      "arn:aws:iam::${data.aws_caller_identity.current.account_id}:role/StepFunctions-${var.environment}-*",
    ]
  }

  statement {
    sid    = "DenyDestructiveActions"
    effect = "Deny"
    actions = [
      "acm:DeleteCertificate",                           # Grants permission to delete a certificate and its associated private key
      "acm:PutAccountConfiguration",                     # Grants permission to update account level configuration in AWS Certificate Manager
      "airflow:DeleteEnvironment",                       # Grants permission to delete an Amazon MWAA environment
      "athena:DeleteDataCatalog",                        # Grants permission to delete a datacatalog
      "cloudformation:DeleteStack",                      # Grants permission to delete a specified stack
      "cloudformation:DeleteStackInstances",             # Grants permission to delete stack instances for the specified accounts, in the specified regions
      "cloudformation:DeleteStackSet",                   # Grants permission to delete a specified stackset
      "cloudfront:DeleteCloudFrontOriginAccessIdentity", # Grants permission to delete a CloudFront origin access identity
      "cloudfront:DeleteDistribution",                   # Grants permission to delete a web distribution
      "cloudfront:DeleteKeyGroup",                       # Grants permission to delete a key group
      "cloudfront:DeletePublicKey",                      # Grants permission to delete a public key from CloudFront
      "cloudfront:DeleteStreamingDistribution",          # Grants permission to delete an RTMP distribution
      "cloudsearch:DeleteDomain",                        # Permanently deletes a search domain and all of its data.
      "dlm:DeleteLifecyclePolicy",                       # Grants permission to delete an existing data lifecycle policy. In addition, this action halts the creation and deletion of snapshots that the policy specified. Existing snapshots are not affected
      "dynamodb:DeleteBackup",                           # Grants permission to delete an existing backup of a table
      "dynamodb:DeleteTable",                            # Grants permission to the DeleteTable operation which deletes a table and all of its items
      "dynamodb:DeleteTableReplica",                     # Grants permission to delete a replica table and all of its items
      "ecs:DeleteAccountSetting",                        # Grants permission to modify the ARN and resource ID format of a resource for a specified IAM user, IAM role, or the root user for an account. You can specify whether the new ARN and resource ID format are disabled for new resources that are created
      "ecs:DeleteCluster",                               # Grants permission to delete the specified cluster
      "ecs:DeleteService",                               # Grants permission to delete a specified service within a cluster
      "elasticache:DeleteCacheCluster",                  # Grants permission to delete a previously provisioned cluster
      "elasticache:DeleteSnapshot",                      # Grants permission to delete an existing snapshot
      "elasticbeanstalk:TerminateEnvironment",           # Grants permission to terminate an environment
      "elasticfilesystem:DeleteFileSystem",              # Grants permission to delete a file system, permanently severing access to its contents
      "elasticloadbalancing:DeleteLoadBalancer",         # Deletes the specified load balancer
      "elasticmapreduce:TerminateJobFlows",              # Grants permission to terminate a cluster (job flow)
      "es:DeleteDomain",                                 # Grants permission to delete an Amazon OpenSearch Service domain and all of its data
      "es:DeleteElasticsearchDomain",                    # Grants permission to delete an OpenSearch Service domain and all of its data. This permission is deprecated. Use DeleteDomain instead
      "glue:BatchDeleteTable",                           # Grants permission to delete one or more tables
      "glue:BatchDeleteTableVersion",                    # Grants permission to delete one or more versions of a table
      "glue:DeleteDatabase",                             # Grants permission to delete a database
      "glue:DeleteTable",                                # Grants permission to delete a table
      "glue:DeleteTableVersion",                         # Grants permission to delete a version of a table
      "kafka:DeleteCluster",                             # Grants permission to delete an MSK cluster
      "kafka:DeleteConfiguration",                       # Grants permission to delete the specified MSK configuration
      "kinesisanalytics:DeleteApplication",              # Deletes the application.
      "kinesis:DeleteStream",                            # Grants permission to delete a stream and all its shards and data
      "kms:DisableKey",                                  # Controls permission to disable an AWS KMS key, which prevents it from being used in cryptographic operations
      "kms:ScheduleKeyDeletion",                         # Controls permission to schedule deletion of an AWS KMS key
      "lambda:DeleteFunction",                           # Grants permission to delete an AWS Lambda function
      "logs:DeleteLogGroup",                             # Grants permissions to delete the log group with the specified name
      "logs:DeleteLogStream",                            # Grants permissions to delete a log stream
      "rds:DeleteDBCluster",                             # Grants permission to delete a previously provisioned DB cluster
      "rds:DeleteDBClusterSnapshot",                     # Grants permission to delete a DB cluster snapshot
      "rds:DeleteDBInstanceAutomatedBackup",             # Grants permission to deletes automated backups based on the source instance's DbiResourceId value or the restorable instance's resource ID
      "rds:DeleteDBInstance",                            # Grants permission to delete a previously provisioned DB instance
      "rds:DeleteDBSnapshot",                            # Grants permission to delete a DBSnapshot
      "rds:DeleteGlobalCluster",                         # Grants permission to delete a global database cluster
      "route53:DeleteHostedZone",                        # Grants permission to delete a hosted zone
      "s3:DeleteBucket",                                 # Grants permission to delete the bucket named in the URI
      "s3:DeleteObjectVersion",                          # Grants permission to remove a specific version of an object
      "sqs:DeleteQueue",                                 # Grants permission to delete the queue specified by the queue URL, regardless of whether the queue is empty
      "states:DeleteStateMachine",                       # Grants permission to delete a state machine
      "wafv2:DeleteFirewallManagerRuleGroups",           # Grants permission to delete FirewallManagedRulesGroups from a WebACL if not managed by Firewall Manager anymore
      "wafv2:DeleteRuleGroup",                           # Grants permission to delete a RuleGroup
      "wafv2:DeleteWebACL",                              # Grants permission to delete a WebACL
    ]
    resources = ["*"]
  }

  statement {
    sid    = "DenyChangesToLoggingConfiguration"
    effect = "Deny"
    actions = [
      "cloudfront:DeleteRealtimeLogConfig", # Grants permission to delete a real-time log configuration
      "cloudfront:UpdateRealtimeLogConfig", # Grants permission to update a real-time log configuration
      "ec2:DeleteFlowLogs",                 # Grants permission to delete one or more flow logs
      "route53:DeleteQueryLoggingConfig",   # Grants permission to delete a configuration for DNS query logging
      "s3:PutBucketLogging",                # Grants permission to set the logging parameters for an Amazon S3 bucket
      "wafv2:DeleteLoggingConfiguration",   # Grants permission to delete the LoggingConfiguration from a WebACL
      "wafv2:PutLoggingConfiguration",      # Grants permission to enable a LoggingConfiguration, to start logging for a web ACL
    ]
    resources = ["*"]
  }

  statement {
    sid    = "DenyDeletingSecretsWithoutRecoveryPeriod"
    effect = "Deny"
    actions = [
      "secretsmanager:DeleteSecret",
    ]
    condition {
      test     = "Bool"
      variable = "secretsmanager:ForceDeleteWithoutRecovery"
      values   = ["true"]
    }
    resources = ["*"]
  }

  statement {
    sid     = "DenyLifecycleChangesToS3BucketsWithLogs"
    effect  = "Deny"
    actions = ["s3:PutLifecycleConfiguration"]
    resources = [
      "arn:aws:s3:::*logs*",
      "arn:aws:s3:::*trail*",
    ]
  }

  statement {
    sid    = "DenyWriteAccessToObjectsInS3BucketsWithLogs"
    effect = "Deny"
    actions = [
      "s3:DeleteObject",
      "s3:DeleteObjectVersion",
      "s3:PutObject",
    ]
    resources = [
      "arn:aws:s3:::*logs*/*",
      "arn:aws:s3:::*trail*/*",
    ]
  }
}

resource "aws_iam_role_policy_attachment" "break_glass_role_elevate_permissions_policy_attachment" {
  role       = aws_iam_role.break_glass_role.name
  policy_arn = aws_iam_policy.break_glass_elevate_permissions_policy.arn
}

# Allow lambda function in the production account validate role in this account
data "aws_iam_policy_document" "elevate_permissions_validate_lambda_role" {
  version = "2012-10-17"
  statement {
    effect = "Allow"
    principals {
      identifiers = [
        module.elevate_permissions_lambda.lambda_role_arn
      ]
      type = "AWS"
    }
    actions = ["sts:AssumeRole"]
  }
}

resource "aws_iam_role" "elevate_permissions_validate_lambda_role" {
  name               = local.break_glass_validate_role_name
  assume_role_policy = data.aws_iam_policy_document.elevate_permissions_validate_lambda_role.json
}

data "aws_iam_policy_document" "elevate_permissions_validate_lambda_policy_document" {
  statement {
    effect = "Allow"
    actions = [
      "iam:GetRole",
    ]
    resources = [
      aws_iam_role.break_glass_role.arn
    ]
  }
}

resource "aws_iam_policy" "elevate_permissions_validate_lambda_policy" {
  name   = "${var.environment}-break-glass-validate"
  policy = data.aws_iam_policy_document.elevate_permissions_validate_lambda_policy_document.json
  tags   = local.tags
}

resource "aws_iam_role_policy_attachment" "elevate_permissions_validate_lambda_policy_attachment" {
  policy_arn = aws_iam_policy.elevate_permissions_validate_lambda_policy.arn
  role       = aws_iam_role.elevate_permissions_validate_lambda_role.name
}
