# Account-global HCP Terraform OIDC provider. An account may hold exactly one # provider per URL. Default is data-source because the frontend stack owns it. resource "aws_iam_openid_connect_provider" "terraform_cloud" { count = var.create_tfc_oidc_provider ? 1 : 0 url = "https://app.terraform.io" client_id_list = ["aws.workload.identity"] thumbprint_list = ["9e99a48a9960b14926bb7f3b02e22da2b0ab7280"] lifecycle { prevent_destroy = true } } data "aws_iam_policy_document" "hcp_plan_assume" { statement { effect = "Allow" actions = ["sts:AssumeRoleWithWebIdentity"] principals { type = "Federated" identifiers = [local.tfc_oidc_arn] } condition { test = "StringEquals" variable = "app.terraform.io:aud" values = ["aws.workload.identity"] } condition { test = "StringEquals" variable = "app.terraform.io:sub" values = ["${local.hcp_sub_prefix}:plan"] } } } data "aws_iam_policy_document" "hcp_apply_assume" { statement { effect = "Allow" actions = ["sts:AssumeRoleWithWebIdentity"] principals { type = "Federated" identifiers = [local.tfc_oidc_arn] } condition { test = "StringEquals" variable = "app.terraform.io:aud" values = ["aws.workload.identity"] } condition { test = "StringEquals" variable = "app.terraform.io:sub" values = ["${local.hcp_sub_prefix}:apply"] } } } data "aws_iam_policy_document" "hcp_refresh" { statement { sid = "RefreshManagedIam" effect = "Allow" actions = [ "iam:GetInstanceProfile", "iam:GetRole", "iam:GetRolePolicy", "iam:ListAttachedRolePolicies", "iam:ListInstanceProfilesForRole", "iam:ListRolePolicies", "iam:ListRoleTags", ] resources = [ local.github_deploy_role_arn, local.eb_ec2_role_arn, local.eb_service_role_arn, "arn:aws:iam::${local.account_id}:instance-profile/tf-managed/${var.eb_ec2_role_name}", ] } statement { sid = "ListOidcProviders" effect = "Allow" actions = ["iam:ListOpenIDConnectProviders"] resources = ["*"] } statement { sid = "ReadGithubOidcProvider" effect = "Allow" actions = ["iam:GetOpenIDConnectProvider"] resources = [local.github_oidc_arn] } statement { sid = "RefreshRds" effect = "Allow" actions = [ "rds:DescribeDBInstances", "rds:DescribeDBParameterGroups", "rds:DescribeDBSubnetGroups", "rds:ListTagsForResource", ] # RDS describe APIs do not support resource-level permissions. resources = ["*"] } statement { sid = "RefreshSecrets" effect = "Allow" actions = [ "secretsmanager:DescribeSecret", "secretsmanager:GetResourcePolicy", "secretsmanager:GetSecretValue", "secretsmanager:ListSecretVersionIds", ] resources = [ "arn:aws:secretsmanager:${var.aws_region}:${local.account_id}:secret:shoc-backend-tf-poc/jwt-JXLaUx", "arn:aws:secretsmanager:${var.aws_region}:${local.account_id}:secret:shoc-backend-tf-poc/webhook-hmac-eThZhu", "arn:aws:secretsmanager:${var.aws_region}:${local.account_id}:secret:rds!db-6e0e2e34-dea1-47b0-8e92-b90bde9cfe20-Mxeu3J", ] } statement { sid = "ListCertificates" effect = "Allow" actions = ["acm:ListCertificates"] resources = ["*"] } statement { sid = "RefreshElasticBeanstalk" effect = "Allow" actions = [ "elasticbeanstalk:DescribeApplications", "elasticbeanstalk:DescribeConfigurationOptions", "elasticbeanstalk:DescribeConfigurationSettings", "elasticbeanstalk:DescribeEnvironmentResources", "elasticbeanstalk:DescribeEnvironments", "elasticbeanstalk:ListTagsForResource", ] # Elastic Beanstalk describe APIs do not support resource-level permissions. resources = ["*"] } statement { sid = "RefreshPocCertificate" effect = "Allow" actions = [ "acm:DescribeCertificate", "acm:GetCertificate", "acm:ListTagsForCertificate", ] resources = ["arn:aws:acm:${var.aws_region}:${local.account_id}:certificate/4fcc2dff-bb11-4204-9107-86d6ce2b95a2"] } statement { sid = "RefreshRoute53" effect = "Allow" actions = [ "route53:GetChange", "route53:GetHostedZone", "route53:ListResourceRecordSets", "route53:ListTagsForResource", ] resources = [ local.hosted_zone_arn, "arn:aws:route53:::change/*", ] } statement { sid = "RefreshNetwork" effect = "Allow" actions = [ "ec2:DescribeSecurityGroupRules", "ec2:DescribeSecurityGroups", "ec2:DescribeSubnets", "ec2:DescribeTags", "ec2:DescribeVpcs", ] # EC2 describe APIs do not support resource-level permissions. resources = ["*"] } } resource "aws_iam_role" "hcp_plan" { name = "hcptf-shoc-backend-tf-poc-plan" description = "HCP Terraform PLAN role for shoc-backend-tf-poc" assume_role_policy = data.aws_iam_policy_document.hcp_plan_assume.json max_session_duration = 3600 permissions_boundary = var.execution_boundary_arn depends_on = [terraform_data.account_guard] lifecycle { prevent_destroy = true } } resource "aws_iam_role_policy" "hcp_plan_refresh" { name = "shoc-backend-tf-poc-plan-refresh" role = aws_iam_role.hcp_plan.id policy = data.aws_iam_policy_document.hcp_refresh.json } resource "aws_iam_role" "hcp_apply" { name = "hcptf-shoc-backend-tf-poc" description = "Read-only HCP Terraform APPLY role for the stabilized shoc-backend-tf-poc" assume_role_policy = data.aws_iam_policy_document.hcp_apply_assume.json max_session_duration = 3600 permissions_boundary = var.execution_boundary_arn depends_on = [terraform_data.account_guard] lifecycle { prevent_destroy = true } } resource "aws_iam_role_policy" "hcp_apply_iam" { name = "shoc-backend-tf-poc-iam" role = aws_iam_role.hcp_apply.id policy = data.aws_iam_policy_document.hcp_refresh.json }