# HCP plan/apply roles for file-share-prod (PLAT-77). # Copy of the syslog-server EC2 shape, narrowed to this stack. # Create, do not import. # # Live seahaven-hcptf-iam-management DenySelfMutation blocks DetachRolePolicy # and PutRolePolicy on hcptf-* (including this role). First-apply sequence: # 1. seahaven-org-baseline scripts/create-hcptf-bootstrap-roles.sh # --account prod --allow-workspace file-share-prod # 2. Point this workspace's TFC_AWS_* at hcptf-bootstrap / # hcptf-bootstrap-plan (workspace vars, never a project set). # 3. One Manual apply. Bootstrap can create these roles and the boundary. # Subnet, DLM, and the instance are created on the following apply # after TFC_AWS_* points at hcptf-file-share. Tolerate that partial # state. # 4. Point TFC_AWS_* back at hcptf-file-share / hcptf-file-share-plan. # 5. Re-run the create script without --allow-workspace to pin trust # back to iam-bootstrap-prod only. # Later apply-role IAM edits use the same window. Do not add StringLike # on bootstrap trust. CreatePolicy stays on hcptf-bootstrap only. data "aws_iam_policy_document" "hcptf_apply_trust" { statement { sid = "HcpApply" effect = "Allow" actions = ["sts:AssumeRoleWithWebIdentity"] principals { type = "Federated" identifiers = ["arn:aws:iam::${data.aws_caller_identity.current.account_id}:oidc-provider/app.terraform.io"] } condition { test = "StringEquals" variable = "app.terraform.io:aud" values = ["aws.workload.identity"] } condition { test = "StringEquals" variable = "app.terraform.io:sub" values = [ "organization:seahaven:project:${local.hcp_project}:workspace:${local.hcp_workspace}:run_phase:apply", ] } } } data "aws_iam_policy_document" "hcptf_plan_trust" { statement { sid = "HcpPlan" effect = "Allow" actions = ["sts:AssumeRoleWithWebIdentity"] principals { type = "Federated" identifiers = ["arn:aws:iam::${data.aws_caller_identity.current.account_id}:oidc-provider/app.terraform.io"] } condition { test = "StringEquals" variable = "app.terraform.io:aud" values = ["aws.workload.identity"] } condition { test = "StringEquals" variable = "app.terraform.io:sub" values = [ "organization:seahaven:project:${local.hcp_project}:workspace:${local.hcp_workspace}:run_phase:plan", ] } } } data "aws_iam_policy_document" "hcptf_scoped_iam" { statement { sid = "DenyCreatePolicy" effect = "Deny" actions = [ "iam:CreatePolicy", "iam:CreatePolicyVersion", "iam:DeletePolicy", "iam:DeletePolicyVersion", "iam:SetDefaultPolicyVersion", ] resources = ["*"] } statement { sid = "CreateExecRoleWithBoundary" effect = "Allow" actions = ["iam:CreateRole"] resources = [ "arn:aws:iam::${data.aws_caller_identity.current.account_id}:role/tf-managed/${local.stack_prefix}*", ] condition { test = "StringLike" variable = "iam:PermissionsBoundary" values = [ "arn:aws:iam::${data.aws_caller_identity.current.account_id}:policy/tf-managed/${local.stack_prefix}*", ] } } statement { sid = "MutateExecRoleWithBoundary" effect = "Allow" actions = [ "iam:AttachRolePolicy", "iam:PutRolePolicy", "iam:PutRolePermissionsBoundary", ] resources = [ "arn:aws:iam::${data.aws_caller_identity.current.account_id}:role/tf-managed/${local.stack_prefix}*", ] condition { test = "StringLike" variable = "iam:PermissionsBoundary" values = [ "arn:aws:iam::${data.aws_caller_identity.current.account_id}:policy/tf-managed/${local.stack_prefix}*", ] } } statement { sid = "WriteExecRoles" effect = "Allow" actions = [ "iam:DeleteRole", "iam:DeleteRolePolicy", "iam:DetachRolePolicy", "iam:TagRole", "iam:UntagRole", "iam:UpdateAssumeRolePolicy", "iam:UpdateRole", "iam:UpdateRoleDescription", ] resources = [ "arn:aws:iam::${data.aws_caller_identity.current.account_id}:role/tf-managed/${local.stack_prefix}*", ] } statement { sid = "PassExecRoleToEc2" effect = "Allow" actions = ["iam:PassRole"] resources = ["arn:aws:iam::${data.aws_caller_identity.current.account_id}:role/tf-managed/${local.instance_role_name}"] condition { test = "StringEquals" variable = "iam:PassedToService" values = ["ec2.amazonaws.com"] } } statement { sid = "PassDlmServiceRole" effect = "Allow" actions = ["iam:PassRole"] resources = [local.dlm_service_role_arn] condition { test = "StringEquals" variable = "iam:PassedToService" values = ["dlm.amazonaws.com"] } } statement { sid = "CreateDlmServiceLinkedRole" effect = "Allow" actions = [ "iam:CreateServiceLinkedRole", ] resources = [local.dlm_service_role_arn] condition { test = "StringEquals" variable = "iam:AWSServiceName" values = ["dlm.amazonaws.com"] } } statement { sid = "InstanceProfiles" effect = "Allow" actions = [ "iam:AddRoleToInstanceProfile", "iam:CreateInstanceProfile", "iam:DeleteInstanceProfile", "iam:GetInstanceProfile", "iam:ListInstanceProfileTags", "iam:RemoveRoleFromInstanceProfile", "iam:TagInstanceProfile", "iam:UntagInstanceProfile", ] resources = [ "arn:aws:iam::${data.aws_caller_identity.current.account_id}:instance-profile/tf-managed/${local.instance_profile_name}", ] } statement { sid = "IamReadOnly" effect = "Allow" actions = [ "iam:GetPolicy", "iam:GetPolicyVersion", "iam:GetRole", "iam:GetRolePolicy", "iam:GetInstanceProfile", "iam:ListAttachedRolePolicies", "iam:ListInstanceProfiles", "iam:ListInstanceProfilesForRole", "iam:ListPolicies", "iam:ListPolicyVersions", "iam:ListRolePolicies", "iam:ListRoleTags", "iam:ListRoles", ] resources = ["*"] } statement { sid = "DenySelfMutation" effect = "Deny" actions = [ "iam:AttachRolePolicy", "iam:DeleteRole", "iam:DeleteRolePolicy", "iam:DeleteRolePermissionsBoundary", "iam:DetachRolePolicy", "iam:PutRolePolicy", "iam:PutRolePermissionsBoundary", "iam:UpdateAssumeRolePolicy", "iam:UpdateRole", "iam:UpdateRoleDescription", ] resources = [ "arn:aws:iam::${data.aws_caller_identity.current.account_id}:role/hcptf-*", "arn:aws:iam::${data.aws_caller_identity.current.account_id}:role/github-cfn-execution-role", "arn:aws:iam::${data.aws_caller_identity.current.account_id}:role/githubdeploy-*", "arn:aws:iam::${data.aws_caller_identity.current.account_id}:role/cdk-hnb659fds-*", "arn:aws:iam::${data.aws_caller_identity.current.account_id}:role/OrganizationAccountAccessRole", "arn:aws:iam::${data.aws_caller_identity.current.account_id}:role/seahaven-*", ] } statement { sid = "DenyBoundaryTampering" effect = "Deny" actions = [ "iam:DeleteRolePermissionsBoundary", "iam:DeleteUserPermissionsBoundary", ] resources = [ "arn:aws:iam::${data.aws_caller_identity.current.account_id}:role/*", "arn:aws:iam::${data.aws_caller_identity.current.account_id}:user/*", ] } statement { sid = "DenyBoundaryPolicyEdit" effect = "Deny" actions = [ "iam:CreatePolicyVersion", "iam:DeletePolicy", "iam:DeletePolicyVersion", "iam:SetDefaultPolicyVersion", ] resources = ["arn:aws:iam::${data.aws_caller_identity.current.account_id}:policy/seahaven-*"] } statement { sid = "ReadTfManagedBoundary" effect = "Allow" actions = [ "iam:GetPolicy", "iam:GetPolicyVersion", "iam:ListPolicyVersions", "iam:ListPolicyTags", "iam:TagPolicy", "iam:UntagPolicy", ] resources = [ "arn:aws:iam::${data.aws_caller_identity.current.account_id}:policy/tf-managed/${local.stack_prefix}*", ] } } data "aws_iam_policy_document" "hcptf_apply_services" { # checkov:skip=CKV_AWS_111: EC2 describe and RunInstances APIs require Resource=*. This role cannot CreateVolume or DeleteVolume. statement { sid = "Ec2Network" effect = "Allow" actions = [ "ec2:AllocateAddress", "ec2:AssociateAddress", "ec2:AssociateRouteTable", "ec2:CreateNatGateway", "ec2:CreateRoute", "ec2:CreateRouteTable", "ec2:AuthorizeSecurityGroupEgress", "ec2:AuthorizeSecurityGroupIngress", "ec2:CreateSecurityGroup", "ec2:CreateSubnet", "ec2:CreateTags", "ec2:DeleteNatGateway", "ec2:DeleteRoute", "ec2:DeleteRouteTable", "ec2:DeleteSecurityGroup", "ec2:DeleteSubnet", "ec2:DeleteTags", "ec2:DescribeAccountAttributes", "ec2:DescribeAddresses", "ec2:DescribeAddressesAttribute", "ec2:DescribeNatGateways", "ec2:DescribeAvailabilityZones", "ec2:DescribeImages", "ec2:DescribeInstanceAttribute", "ec2:DescribeInstanceCreditSpecifications", "ec2:DescribeInstanceStatus", "ec2:DescribeInstanceTypes", "ec2:DescribeInstances", "ec2:DescribeInternetGateways", "ec2:DescribeNetworkInterfaces", "ec2:DescribePrefixLists", "ec2:DescribeRouteTables", "ec2:DescribeSecurityGroupRules", "ec2:DescribeSecurityGroups", "ec2:DescribeSubnets", "ec2:DescribeTags", "ec2:DescribeVolumeAttribute", "ec2:DescribeVolumeStatus", "ec2:DescribeVolumes", "ec2:DescribeVpcAttribute", "ec2:DescribeVpcs", "ec2:DescribeVpnGateways", "ec2:DisassociateAddress", "ec2:DisassociateRouteTable", "ec2:ReleaseAddress", "ec2:ModifySecurityGroupRules", "ec2:ModifySubnetAttribute", "ec2:RevokeSecurityGroupEgress", "ec2:RevokeSecurityGroupIngress", "ec2:UpdateSecurityGroupRuleDescriptionsEgress", "ec2:UpdateSecurityGroupRuleDescriptionsIngress", ] resources = ["*"] } statement { sid = "Ec2Instance" effect = "Allow" actions = [ "ec2:AssociateIamInstanceProfile", "ec2:AttachVolume", "ec2:DescribeIamInstanceProfileAssociations", "ec2:DetachVolume", "ec2:DisassociateIamInstanceProfile", "ec2:GetConsoleOutput", "ec2:ModifyInstanceAttribute", "ec2:MonitorInstances", "ec2:ReplaceIamInstanceProfileAssociation", "ec2:RunInstances", "ec2:StartInstances", "ec2:StopInstances", "ec2:TerminateInstances", "ec2:UnmonitorInstances", ] resources = ["*"] } statement { sid = "DlmPolicy" effect = "Allow" actions = [ "dlm:CreateLifecyclePolicy", "dlm:DeleteLifecyclePolicy", "dlm:GetLifecyclePolicy", "dlm:ListTagsForResource", "dlm:TagResource", "dlm:UntagResource", "dlm:UpdateLifecyclePolicy", ] resources = ["*"] } } data "aws_iam_policy_document" "hcptf_plan_refresh" { statement { sid = "RefreshIamRoles" effect = "Allow" actions = [ "iam:GetRole", "iam:GetRolePolicy", "iam:GetInstanceProfile", "iam:ListRolePolicies", "iam:ListAttachedRolePolicies", "iam:ListInstanceProfilesForRole", "iam:ListRoleTags", ] resources = [ "arn:aws:iam::${data.aws_caller_identity.current.account_id}:role/tf-managed/${local.stack_prefix}*", "arn:aws:iam::${data.aws_caller_identity.current.account_id}:instance-profile/tf-managed/${local.stack_prefix}*", "arn:aws:iam::${data.aws_caller_identity.current.account_id}:role/${local.apply_role}", "arn:aws:iam::${data.aws_caller_identity.current.account_id}:role/${local.plan_role}", ] } statement { sid = "RefreshManagedPolicies" effect = "Allow" actions = [ "iam:GetPolicy", "iam:GetPolicyVersion", ] resources = [ "arn:aws:iam::${data.aws_caller_identity.current.account_id}:policy/tf-managed/${local.stack_prefix}*", "arn:aws:iam::aws:policy/AmazonSSMManagedInstanceCore", "arn:aws:iam::aws:policy/job-function/ViewOnlyAccess", ] } statement { sid = "RefreshEc2" effect = "Allow" actions = [ "ec2:DescribeAccountAttributes", "ec2:DescribeAddresses", "ec2:DescribeAddressesAttribute", "ec2:DescribeAvailabilityZones", "ec2:DescribeIamInstanceProfileAssociations", "ec2:DescribeImages", "ec2:DescribeInstanceAttribute", "ec2:DescribeInstanceCreditSpecifications", "ec2:DescribeInstanceStatus", "ec2:DescribeInstanceTypes", "ec2:DescribeInstances", "ec2:DescribeInternetGateways", "ec2:DescribeNatGateways", "ec2:DescribeNetworkInterfaces", "ec2:DescribePrefixLists", "ec2:DescribeRouteTables", "ec2:DescribeSecurityGroupRules", "ec2:DescribeSecurityGroups", "ec2:DescribeSubnets", "ec2:DescribeTags", "ec2:DescribeVolumeAttribute", "ec2:DescribeVolumeStatus", "ec2:DescribeVolumes", "ec2:DescribeVpcAttribute", "ec2:DescribeVpcs", "ec2:DescribeVpnGateways", "ec2:GetConsoleOutput", ] resources = ["*"] } statement { sid = "RefreshDlm" effect = "Allow" actions = [ "dlm:GetLifecyclePolicy", "dlm:ListTagsForResource", ] resources = ["*"] } } resource "aws_iam_role" "hcptf_apply" { name = local.apply_role assume_role_policy = data.aws_iam_policy_document.hcptf_apply_trust.json max_session_duration = 3600 tags = { Owner = "adam@seahavenind.com" ManagedBy = "terraform" } } resource "aws_iam_role" "hcptf_plan" { name = local.plan_role assume_role_policy = data.aws_iam_policy_document.hcptf_plan_trust.json max_session_duration = 3600 tags = { Owner = "adam@seahavenind.com" ManagedBy = "terraform" } } resource "aws_iam_role_policy" "hcptf_scoped_iam" { name = "scoped-iam-management" role = aws_iam_role.hcptf_apply.id policy = data.aws_iam_policy_document.hcptf_scoped_iam.json } resource "aws_iam_role_policy" "hcptf_apply_services" { # checkov:skip=CKV_AWS_111: EC2 describe and RunInstances APIs require Resource=*. This role cannot CreateVolume or DeleteVolume. name = "file-share-services" role = aws_iam_role.hcptf_apply.id policy = data.aws_iam_policy_document.hcptf_apply_services.json } resource "aws_iam_role_policy" "hcptf_plan_refresh" { # checkov:skip=CKV_AWS_107: ViewOnlyAccess plus this sidecar is the HCP plan-role pattern. Actions are named. It does not add iam:CreateAccessKey, secretsmanager:GetSecretValue, or ssm:GetParameter on *. name = "file-share-plan-refresh" role = aws_iam_role.hcptf_plan.id policy = data.aws_iam_policy_document.hcptf_plan_refresh.json } resource "aws_iam_role_policy_attachment" "hcptf_plan_view_only" { role = aws_iam_role.hcptf_plan.name policy_arn = "arn:aws:iam::aws:policy/job-function/ViewOnlyAccess" } resource "aws_iam_role_policy_attachments_exclusive" "hcptf_apply" { role_name = aws_iam_role.hcptf_apply.name policy_arns = [] } resource "aws_iam_role_policy_attachments_exclusive" "hcptf_plan" { role_name = aws_iam_role.hcptf_plan.name policy_arns = [ "arn:aws:iam::aws:policy/job-function/ViewOnlyAccess", ] }