diff --git a/services/terraform/remote/aws_iam.tf b/services/terraform/remote/aws_iam.tf index 9983adec2..98097ff91 100644 --- a/services/terraform/remote/aws_iam.tf +++ b/services/terraform/remote/aws_iam.tf @@ -1,321 +1,337 @@ ### General AWS Utility IAM resources # Docs: https://docs.aws.amazon.com/AmazonECS/latest/developerguide/instance_IAM_role.html resource "aws_iam_role" "ecs_instance_role" { name = "ecsInstanceRole" description = "Allows EC2 instances to call AWS services on your behalf." assume_role_policy = jsonencode({ Version = "2012-10-17" Statement = [ { Action = "sts:AssumeRole" Effect = "Allow" Principal = { Service = "ec2.amazonaws.com" } } ] }) managed_policy_arns = [ "arn:aws:iam::aws:policy/service-role/AmazonEC2ContainerServiceforEC2Role", # Let instances download Docker images from ECR "arn:aws:iam::aws:policy/AmazonEC2ContainerRegistryReadOnly" ] } # ECS Task execution role # Docs: https://docs.aws.amazon.com/AmazonECS/latest/developerguide/task_execution_IAM_role.html resource "aws_iam_role" "ecs_task_execution" { name = "ecsTaskExecutionRole" assume_role_policy = jsonencode({ Version = "2008-10-17" Statement = [ { Sid = "" Action = "sts:AssumeRole" Effect = "Allow" Principal = { Service = "ecs-tasks.amazonaws.com" } } ] }) managed_policy_arns = [ "arn:aws:iam::aws:policy/AmazonSSMReadOnlyAccess", "arn:aws:iam::aws:policy/service-role/AmazonECSTaskExecutionRolePolicy", # Let ECS write logs to CloudWatch "arn:aws:iam::aws:policy/CloudWatchLogsFullAccess", # Let ECS tasks access secrets to expose them as env vars "arn:aws:iam::aws:policy/SecretsManagerReadWrite", ] } # Assume Role Policy Document for EC2 and ECS # This policy allows ECS and EC2 use roles that it is assigned to data "aws_iam_policy_document" "assume_role_ecs_ec2" { statement { effect = "Allow" actions = [ "sts:AssumeRole", ] principals { type = "Service" identifiers = [ "ec2.amazonaws.com", "ecs-tasks.amazonaws.com" ] } } } # Allows ECS Exec to SSH into service task containers resource "aws_iam_policy" "allow_ecs_exec" { name = "allow-ecs-exec" description = "Adds SSM permissions to enable ECS Exec" policy = jsonencode({ Version = "2012-10-17" Statement = [ { Effect = "Allow" Action = [ "ssmmessages:CreateControlChannel", "ssmmessages:CreateDataChannel", "ssmmessages:OpenControlChannel", "ssmmessages:OpenDataChannel" ] Resource = "*" } ] }) } ### App IAM resources # Our app role - this is to give access to DynamoDB etc # Has trust policy with EC2 and ECS # Also allows to SSH into containers resource "aws_iam_role" "services_ddb_full_access" { name = "dynamodb-s3-full-access" description = "Full RW access to DDB and S3. Allows to SSH into ECS containers" assume_role_policy = data.aws_iam_policy_document.assume_role_ecs_ec2.json managed_policy_arns = [ aws_iam_policy.allow_ecs_exec.arn, "arn:aws:iam::aws:policy/AmazonDynamoDBFullAccess", "arn:aws:iam::aws:policy/AmazonS3FullAccess", ] } # Feature Flags IAM data "aws_iam_policy_document" "read_feature_flags" { statement { sid = "FeatureFlagsDDBReadAccess" effect = "Allow" actions = [ "dynamodb:BatchGetItem", "dynamodb:GetItem", "dynamodb:Query", "dynamodb:Scan", ] resources = [ module.shared.dynamodb_tables["feature-flags"].arn ] } } resource "aws_iam_policy" "read_feature_flags" { name = "feature-flags-ddb-read-access" policy = data.aws_iam_policy_document.read_feature_flags.json description = "Allows full read access to feature-flags DynamoDB table" } resource "aws_iam_role" "feature_flags_service" { name = "feature-flags-service-role" assume_role_policy = data.aws_iam_policy_document.assume_role_ecs_ec2.json managed_policy_arns = [ aws_iam_policy.read_feature_flags.arn ] } # Backup Service IAM data "aws_iam_policy_document" "manage_backup_ddb" { statement { sid = "BackupFullDDBAccess" effect = "Allow" actions = [ "dynamodb:*", ] resources = [ module.shared.dynamodb_tables["backup-service-backup"].arn, "${module.shared.dynamodb_tables["backup-service-backup"].arn}/index/*" ] } } resource "aws_iam_policy" "manage_backup_ddb" { name = "backup-ddb-full-access" policy = data.aws_iam_policy_document.manage_backup_ddb.json description = "Allows full access to backup DynamoDB table" } resource "aws_iam_role" "backup_service" { name = "backup-service-role" assume_role_policy = data.aws_iam_policy_document.assume_role_ecs_ec2.json managed_policy_arns = [ aws_iam_policy.allow_ecs_exec.arn, aws_iam_policy.manage_backup_ddb.arn ] } # Reports Service IAM data "aws_iam_policy_document" "manage_reports_ddb" { statement { sid = "ReportsFullDDBAccess" effect = "Allow" actions = [ "dynamodb:*", ] resources = [ module.shared.dynamodb_tables["reports-service-reports"].arn ] } } resource "aws_iam_policy" "manage_reports_ddb" { name = "reports-ddb-full-access" policy = data.aws_iam_policy_document.manage_reports_ddb.json description = "Allows full access to reports DynamoDB table" } resource "aws_iam_role" "reports_service" { name = "reports-service-role" assume_role_policy = data.aws_iam_policy_document.assume_role_ecs_ec2.json managed_policy_arns = [ aws_iam_policy.allow_ecs_exec.arn, aws_iam_policy.manage_reports_ddb.arn ] } data "aws_iam_policy_document" "assume_identity_search_role" { statement { effect = "Allow" principals { type = "Service" identifiers = ["lambda.amazonaws.com"] } actions = ["sts:AssumeRole"] } } resource "aws_iam_role" "search_index_lambda" { name = "search_index_lambda" assume_role_policy = data.aws_iam_policy_document.assume_identity_search_role.json managed_policy_arns = [ aws_iam_policy.manage_cloudwatch_logs.arn, aws_iam_policy.manage_network_interface.arn, aws_iam_policy.read_identity_users_stream.arn, ] } data "aws_iam_policy_document" "read_identity_users_stream" { statement { effect = "Allow" actions = [ "dynamodb:GetRecords", "dynamodb:GetShardIterator", "dynamodb:DescribeStream", "dynamodb:ListStreams", ] resources = [ module.shared.dynamodb_tables["identity-users"].stream_arn, "${module.shared.dynamodb_tables["identity-users"].arn}/stream/*", module.shared.dynamodb_tables["identity-reserved-usernames"].stream_arn, "${module.shared.dynamodb_tables["identity-reserved-usernames"].arn}/stream/*", ] } } resource "aws_iam_policy" "read_identity_users_stream" { name = "read-identity-users-stream" path = "/" description = "IAM policy for managing identity-users stream" policy = data.aws_iam_policy_document.read_identity_users_stream.json } data "aws_iam_policy_document" "manage_cloudwatch_logs" { statement { effect = "Allow" actions = [ "logs:CreateLogGroup", "logs:CreateLogStream", "logs:PutLogEvents", ] resources = ["arn:aws:logs:*:*:*"] } } resource "aws_iam_policy" "manage_cloudwatch_logs" { name = "manage-cloudwatch-logs" path = "/" description = "IAM policy for managing cloudwatch logs" policy = data.aws_iam_policy_document.manage_cloudwatch_logs.json } data "aws_iam_policy_document" "manage_network_interface" { statement { effect = "Allow" actions = [ "ec2:CreateNetworkInterface", "ec2:DescribeNetworkInterfaces", "ec2:DeleteNetworkInterface" ] resources = ["*"] } } resource "aws_iam_policy" "manage_network_interface" { name = "manage-network-interface" path = "/" description = "IAM policy for managing network interfaces" policy = data.aws_iam_policy_document.manage_network_interface.json } resource "aws_iam_role_policy_attachment" "AWSLambdaVPCAccessExecutionRole" { role = aws_iam_role.search_index_lambda.name policy_arn = "arn:aws:iam::aws:policy/service-role/AWSLambdaVPCAccessExecutionRole" } data "aws_iam_policy_document" "opensearch_domain_access" { statement { effect = "Allow" principals { type = "*" identifiers = ["${module.shared.search_index_lambda.arn}"] } actions = [ "es:ESHttpHead", "es:ESHttpPost", "es:ESHttpGet", "es:ESHttpDelete", "es:ESHttpPut", ] resources = ["${module.shared.opensearch_domain_identity.arn}/*"] } } resource "aws_opensearch_domain_policy" "opensearch_domain_access" { domain_name = module.shared.opensearch_domain_identity.domain_name access_policies = data.aws_iam_policy_document.opensearch_domain_access.json } + +resource "aws_iam_role" "task_scheduler" { + name = "cron-scheduler-role" + assume_role_policy = jsonencode({ + Version = "2012-10-17" + Statement = [ + { + Effect = "Allow" + Principal = { + Service = ["scheduler.amazonaws.com"] + } + Action = "sts:AssumeRole" + } + ] + }) +} diff --git a/services/terraform/remote/task_blob_cleanup.tf b/services/terraform/remote/task_blob_cleanup.tf index d32c3299f..4cde089ec 100644 --- a/services/terraform/remote/task_blob_cleanup.tf +++ b/services/terraform/remote/task_blob_cleanup.tf @@ -1,132 +1,116 @@ locals { # Run every day at midnight UTC blob_cleanup_enabled = true blob_cleanup_schedule = "cron(0 0 * * ? *)" } resource "aws_ecs_task_definition" "blob_cleanup" { family = "blob-cleanup-task-def" container_definitions = jsonencode([ { essential = true name = local.blob_service_container_name image = local.blob_service_server_image command = ["blob", "cleanup"] environment = [ { name = "RUST_LOG" value = local.is_staging ? "info,blob=trace,comm_lib=debug" : "info" }, { name = "BLOB_S3_BUCKET_NAME", value = local.blob_service_s3_bucket } ] logConfiguration = { "logDriver" = "awslogs" "options" = { "awslogs-create-group" = "true" "awslogs-group" = "/ecs/blob-cleanup" "awslogs-region" = "us-east-2" "awslogs-stream-prefix" = "ecs" } } } ]) task_role_arn = aws_iam_role.services_ddb_full_access.arn execution_role_arn = aws_iam_role.ecs_task_execution.arn network_mode = "awsvpc" cpu = "256" memory = "512" requires_compatibilities = ["FARGATE"] skip_destroy = false } resource "aws_scheduler_schedule" "blob_cleanup" { name = "blob-cleanup-schedule" group_name = "default" schedule_expression = local.blob_cleanup_schedule state = local.blob_cleanup_enabled ? "ENABLED" : "DISABLED" # Task can run within 15 minutes window of the scheduled time flexible_time_window { mode = "FLEXIBLE" maximum_window_in_minutes = 15 } target { arn = aws_ecs_cluster.comm_services.arn - role_arn = aws_iam_role.scheduler.arn + role_arn = aws_iam_role.task_scheduler.arn ecs_parameters { task_definition_arn = aws_ecs_task_definition.blob_cleanup.arn_without_revision launch_type = "FARGATE" network_configuration { assign_public_ip = true security_groups = [aws_security_group.blob_service.id] subnets = [ aws_subnet.public_a.id, aws_subnet.public_b.id, aws_subnet.public_c.id, ] } } retry_policy { maximum_event_age_in_seconds = 300 maximum_retry_attempts = 5 } } } -resource "aws_iam_role" "scheduler" { - name = "cron-scheduler-role" - assume_role_policy = jsonencode({ - Version = "2012-10-17" - Statement = [ - { - Effect = "Allow" - Principal = { - Service = ["scheduler.amazonaws.com"] - } - Action = "sts:AssumeRole" - } - ] - }) -} - -resource "aws_iam_role_policy_attachment" "scheduler" { - policy_arn = aws_iam_policy.scheduler.arn - role = aws_iam_role.scheduler.name +resource "aws_iam_role_policy_attachment" "blob_cleanup_scheduler" { + policy_arn = aws_iam_policy.blob_cleanup_scheduler.arn + role = aws_iam_role.task_scheduler.name } -resource "aws_iam_policy" "scheduler" { - name = "cron-scheduler-policy" +resource "aws_iam_policy" "blob_cleanup_scheduler" { + name = "blob-cleanup-cron-scheduler-policy" policy = jsonencode({ Version = "2012-10-17" Statement = [ # Allow scheduler to execute the task { Effect = "Allow", Action = [ "ecs:RunTask" ] Resource = aws_ecs_task_definition.blob_cleanup.arn_without_revision }, # Allow scheduler to set the IAM roles of the ECS task { Effect = "Allow", Action = [ "iam:PassRole" ] Resource = [ aws_ecs_task_definition.blob_cleanup.execution_role_arn, aws_ecs_task_definition.blob_cleanup.task_role_arn ] }, ] }) }