# Pipeline Jenkins.
locals {
  # To avoid the 6144 character limit on IAM policies, conservatively chunk the roles to assume into smaller lists.
  chunked_pipeline_roles = chunklist(var.pipeline_roles_to_assume, 50)
}

# Role.
data "aws_iam_role" "pipeline" {
  name = local.pipeline_role_name
}

# Access role.
resource "aws_iam_policy" "pipeline_access_policy" {
  name   = "${var.environment}-${var.pipeline_service_name}-access-policy"
  policy = data.aws_iam_policy_document.pipeline_access_policy.json

  tags = {
    environment        = var.environment
    service_name       = var.pipeline_service_name
    application_family = var.application_family
  }
}

data "aws_iam_policy_document" "pipeline_access_policy" {
  # checkov:skip=CKV_AWS_109:Allow "*" resources in this policy for Jenkins. Critical grant statements are protected by IAM conditions.
  # checkov:skip=CKV_AWS_110:Allow "*" resources in this policy for Jenkins. Critical grant statements are protected by IAM conditions.
  # checkov:skip=CKV_AWS_111:Allow "*" resources in this policy for Jenkins. Critical grant statements are protected by IAM conditions.

  statement {
    sid    = "AllowECRRepositoryCreation"
    effect = "Allow"
    actions = [
      "ecr:CreateRepository",
      "ecr:PutImageScanningConfiguration",
    ]
    resources = ["*"]
  }


  statement {
    sid    = "AllowECRImageOperations"
    effect = "Allow"
    actions = [
      "ecr:BatchCheckLayerAvailability",
      "ecr:BatchGetImage",
      "ecr:CompleteLayerUpload",
      "ecr:DescribeImages",
      "ecr:DescribeImageScanFindings",
      "ecr:DescribeRepositories",
      "ecr:GetAuthorizationToken",
      "ecr:GetDownloadUrlForLayer",
      "ecr:GetLifecyclePolicy",
      "ecr:GetLifecyclePolicyPreview",
      "ecr:GetRepositoryPolicy",
      "ecr:InitiateLayerUpload",
      "ecr:ListImages",
      "ecr:ListTagsForResource",
      "ecr:PutImage",
      "ecr:UploadLayerPart",
    ]
    resources = ["*"]
  }

  statement {
    sid    = "AllowPythonDeploymentUtilsFargateDeployScript"
    effect = "Allow"
    actions = [
      "application-autoscaling:DescribeScalableTargets",
      "application-autoscaling:RegisterScalableTarget",
      "cloudwatch:DescribeAlarms",
      "cloudwatch:DisableAlarmActions",
      "cloudwatch:EnableAlarmActions",
      "ecs:DescribeServices",
      "ecs:DescribeTaskDefinition",
      "ecs:DescribeTasks",
      "ecs:ListTasks",
      "ecs:RegisterTaskDefinition",
      "ecs:TagResource",
      "ecs:UpdateService",
      "events:ListTargetsByRule",
      "events:PutTargets",
      "iam:PassRole",
    ]
    resources = ["*"]
  }

  statement {
    sid    = "AllowLambdaDeployment"
    effect = "Allow"
    actions = [
      "lambda:CreateAlias",
      "lambda:Get*",
      "lambda:List*",
      "lambda:PublishVersion",
      "lambda:UpdateAlias",
      "lambda:UpdateFunctionCode",
    ]
    resources = ["*"]
  }

  statement {
    sid     = "AllowPassRoleToECS"
    effect  = "Allow"
    actions = ["iam:PassRole"]
    condition {
      test     = "StringLike"
      variable = "iam:PassedToService"
      values   = ["ecs-tasks.amazonaws.com"]
    }
    resources = [
      "arn:aws:iam::${data.aws_caller_identity.current.account_id}:role/qa-*-task-role",
      "arn:aws:iam::${data.aws_caller_identity.current.account_id}:role/qa-*-execution-role",
      "arn:aws:iam::${data.aws_caller_identity.current.account_id}:role/prod-*-task-role",
      "arn:aws:iam::${data.aws_caller_identity.current.account_id}:role/prod-*-execution-role",
    ]
  }

  statement {
    sid    = "AllowUsingKMSKeysViaLambdaService"
    effect = "Allow"
    actions = [
      "kms:CreateGrant",
      "kms:Decrypt",
      "kms:DescribeKey",
      "kms:Encrypt",
      "kms:GenerateDataKey*",
    ]
    condition {
      test     = "StringLike"
      variable = "kms:ViaService"
      values   = ["lambda.${var.aws_region}.amazonaws.com"]
    }
    resources = ["*"]
  }
}

resource "aws_iam_role_policy_attachment" "pipeline_access_policy_attachment" {
  role       = data.aws_iam_role.pipeline.id
  policy_arn = aws_iam_policy.pipeline_access_policy.arn
}

# ECR access.
resource "aws_iam_policy" "pipeline_ecr_policy" {
  name   = "ECR-${var.environment}-${var.pipeline_service_name}-ReadOnly-policy"
  policy = data.aws_iam_policy_document.pipeline_ecr_policy.json

  tags = {
    environment        = var.environment
    service_name       = var.pipeline_service_name
    application_family = var.application_family
  }
}

data "aws_iam_policy_document" "pipeline_ecr_policy" {
  statement {
    effect = "Allow"
    actions = [
      "ecr:BatchGetImage",
      "ecr:Describe*",
      "ecr:Get*",
      "ecr:ListImages",
    ]
    resources = ["*"]
  }
}

resource "aws_iam_role_policy_attachment" "pipeline_ecr_policy_attachment" {
  role       = data.aws_iam_role.pipeline.id
  policy_arn = aws_iam_policy.pipeline_ecr_policy.arn
}

# AssumeRole access.
resource "aws_iam_policy" "pipeline_assume_role_policy" {
  count = length(local.chunked_pipeline_roles)

  name   = "STS-${local.pipeline_role_name}-AssumeRole-policy-${count.index}"
  policy = data.aws_iam_policy_document.pipeline_assume_role_policy[count.index].json

  tags = {
    environment        = var.environment
    service_name       = var.pipeline_service_name
    application_family = var.application_family
  }
}

data "aws_iam_policy_document" "pipeline_assume_role_policy" {
  count = length(local.chunked_pipeline_roles)

  statement {
    effect  = "Allow"
    actions = ["sts:AssumeRole"]
    resources = concat(
      local.chunked_pipeline_roles[count.index]
    )
  }
}

resource "aws_iam_role_policy_attachment" "pipeline_assume_role_policy_attachment" {
  count = length(local.chunked_pipeline_roles)

  role       = data.aws_iam_role.pipeline.id
  policy_arn = aws_iam_policy.pipeline_assume_role_policy[count.index].arn
}

# Allow role to assume itself to facilitate consistency between intra-account and inter-account operations
data "aws_iam_policy_document" "pipeline_assume_self_role_policy" {
  statement {
    effect  = "Allow"
    actions = ["sts:AssumeRole"]
    resources = [data.aws_iam_role.pipeline.arn]
  }
}

resource "aws_iam_role_policy" "pipeline_assume_self_role_policy" {
  name   = "${var.environment}-${var.pipeline_service_name}-assume-self-role-policy"
  policy = data.aws_iam_policy_document.pipeline_assume_self_role_policy.json
  role   = data.aws_iam_role.pipeline.id
}
