data "aws_iam_openid_connect_provider" "github" { url = "https://token.actions.githubusercontent.com" } locals { application_arn = "arn:aws:elasticbeanstalk:${var.aws_region}:${var.aws_account_id}:application/${var.eb_application_name}" environment_arn = "arn:aws:elasticbeanstalk:${var.aws_region}:${var.aws_account_id}:environment/${var.eb_application_name}/${var.eb_environment_name}" environment_stack_name = "awseb-${var.eb_environment_id}-stack" environment_stack_arn = "arn:aws:cloudformation:${var.aws_region}:${var.aws_account_id}:stack/${local.environment_stack_name}/*" environment_asg_arn = "arn:aws:autoscaling:${var.aws_region}:${var.aws_account_id}:autoScalingGroup:*:autoScalingGroupName/${local.environment_stack_name}-*" eb_bucket_name = "elasticbeanstalk-${var.aws_region}-${var.aws_account_id}" use_legacy_s3_policy = !var.adoption_complete && var.legacy_dev_s3_policy } data "aws_iam_policy_document" "assume" { statement { effect = "Allow" actions = ["sts:AssumeRoleWithWebIdentity"] principals { type = "Federated" identifiers = [data.aws_iam_openid_connect_provider.github.arn] } condition { test = "StringEquals" variable = "token.actions.githubusercontent.com:aud" values = ["sts.amazonaws.com"] } condition { test = "StringEquals" variable = "token.actions.githubusercontent.com:sub" values = ["repo:${var.github_repo}:environment:${var.environment}"] } } } resource "aws_iam_role" "github_deploy" { name = var.github_deploy_role_name path = "/" description = var.adoption_complete ? ( "Least-privilege GitHub OIDC deploy role for shoc-backend ${var.environment}. Terraform-owned; application/environment/S3 are owned by Elastic Beanstalk." ) : ( "Least-privilege GitHub OIDC deploy role for shoc-backend ${var.environment}. CDK-owned; application/environment/S3 are owned by Elastic Beanstalk." ) assume_role_policy = data.aws_iam_policy_document.assume.json max_session_duration = 3600 tags = { Component = "deploy-role" Environment = var.environment ManagedBy = var.adoption_complete ? "terraform" : "cdk" Project = "shoc-backend" } lifecycle { prevent_destroy = true } } data "aws_iam_policy_document" "deploy" { statement { effect = "Allow" actions = [ "autoscaling:Describe*", "ec2:Describe*", "elasticbeanstalk:DescribeApplicationVersions", "elasticbeanstalk:DescribeEnvironments", "elasticbeanstalk:DescribeEvents", "elasticloadbalancing:Describe*", ] # These AWS read APIs do not support resource-level permissions. resources = ["*"] } statement { effect = "Allow" actions = ["elasticbeanstalk:CreateApplicationVersion"] resources = [ local.application_arn, "arn:aws:elasticbeanstalk:${var.aws_region}:${var.aws_account_id}:applicationversion/${var.eb_application_name}/*", ] } statement { effect = "Allow" actions = ["elasticbeanstalk:UpdateEnvironment"] resources = [local.environment_arn] } statement { effect = "Allow" actions = [ "cloudformation:CancelUpdateStack", "cloudformation:DescribeStackEvents", "cloudformation:DescribeStackResource", "cloudformation:DescribeStackResources", "cloudformation:DescribeStacks", "cloudformation:GetTemplate", "cloudformation:ListStackResources", "cloudformation:UpdateStack", ] resources = [local.environment_stack_arn] } statement { effect = "Allow" actions = [ "autoscaling:PutNotificationConfiguration", "autoscaling:ResumeProcesses", "autoscaling:SuspendProcesses", ] resources = [local.environment_asg_arn] } dynamic "statement" { for_each = local.use_legacy_s3_policy ? [1] : [] content { effect = "Allow" actions = ["s3:Delete*", "s3:Get*", "s3:Put*"] resources = [ "arn:aws:s3:::elasticbeanstalk-*/*", ] } } dynamic "statement" { for_each = local.use_legacy_s3_policy ? [1] : [] content { effect = "Allow" actions = [ "s3:GetBucket*", "s3:ListBucket", "s3:PutBucketOwnershipControls", "s3:PutBucketPolicy", "s3:PutBucketPublicAccessBlock", ] resources = ["arn:aws:s3:::elasticbeanstalk-*"] } } dynamic "statement" { for_each = local.use_legacy_s3_policy ? [] : [1] content { effect = "Allow" actions = ["s3:PutObject"] resources = ["arn:aws:s3:::${local.eb_bucket_name}/${var.eb_application_name}/*"] } } dynamic "statement" { for_each = local.use_legacy_s3_policy ? [] : [1] content { effect = "Allow" actions = [ "s3:GetBucketLocation", "s3:ListBucket", ] resources = ["arn:aws:s3:::${local.eb_bucket_name}"] } } } resource "aws_iam_role_policy" "github_deploy" { name = var.policy_name role = aws_iam_role.github_deploy.id policy = data.aws_iam_policy_document.deploy.json lifecycle { prevent_destroy = true } }