mirror of
https://github.com/Sea-Haven-Industries/seahaven-org-baseline.git
synced 2026-09-30 03:23:15 +00:00
A first apply of Fargate and Scheduler targets cannot create those service-linked attachments while PassRole is Lambda-only.
264 lines
8.6 KiB
Text
264 lines
8.6 KiB
Text
# Example: import an existing prod/dev hcptf-<stack> pair into app Terraform
|
|
# (PLAT-146). Copy into the consumer repo's terraform/ directory. Replace
|
|
# locals, then `terraform import` (or keep the import blocks) on a Manual
|
|
# apply. Do not recreate the role. Role names stay `hcptf-STACK` /
|
|
# `hcptf-STACK-plan`.
|
|
#
|
|
# Live `seahaven-hcptf-iam-management` DenySelfMutation blocks DetachRolePolicy
|
|
# and PutRolePolicy on hcptf-* (including this role). The stack workspace
|
|
# cannot apply this file while TFC_AWS_* still points at hcptf-STACK, and
|
|
# hcptf-bootstrap trust is exact StringEquals for workspace
|
|
# iam-bootstrap-<env> only (HCP names are org-unique). Import apply sequence:
|
|
# 1. scripts/create-hcptf-bootstrap-roles.sh --account prod|dev \
|
|
# --allow-workspace STACK-prod
|
|
# 2. Point this workspace's TFC_AWS_* at hcptf-bootstrap /
|
|
# hcptf-bootstrap-plan (workspace vars, never a project set).
|
|
# 3. One Manual apply (import + detach seahaven-hcptf-iam-management +
|
|
# put scoped inline).
|
|
# 4. Point TFC_AWS_* back at hcptf-STACK / hcptf-STACK-plan.
|
|
# 5. Re-run the script without --allow-workspace to pin trust back to
|
|
# iam-bootstrap-<env> only.
|
|
# Later apply-role IAM edits use the same window. Do not add StringLike
|
|
# on bootstrap trust.
|
|
#
|
|
# SAM-only repos and SHOC/external-dev do not use this file.
|
|
|
|
locals {
|
|
account_id = "011934824531" # seahaven-prod; use 710827005802 for seahaven-dev
|
|
hcp_project = "seahaven-prod"
|
|
hcp_workspace = "STACK-prod"
|
|
apply_role = "hcptf-STACK"
|
|
plan_role = "hcptf-STACK-plan"
|
|
stack_name = "STACK"
|
|
stack_prefix = "STACK-"
|
|
}
|
|
|
|
data "aws_iam_policy_document" "hcptf_apply_trust" {
|
|
statement {
|
|
effect = "Allow"
|
|
actions = ["sts:AssumeRoleWithWebIdentity"]
|
|
principals {
|
|
type = "Federated"
|
|
identifiers = ["arn:aws:iam::${local.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 {
|
|
effect = "Allow"
|
|
actions = ["sts:AssumeRoleWithWebIdentity"]
|
|
principals {
|
|
type = "Federated"
|
|
identifiers = ["arn:aws:iam::${local.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",
|
|
]
|
|
}
|
|
}
|
|
}
|
|
|
|
# Rendered from lib/hcptf-bootstrap/scoped-apply-iam.json.tmpl. CreatePolicy
|
|
# stays on hcptf-bootstrap only. Exec-role writes are prefix-scoped. Boundary
|
|
# ARNs are StringLike-pinned (not Null); AdministratorAccess is not accepted.
|
|
# The role cannot PutRolePolicy on hcptf-* (including itself).
|
|
data "aws_iam_policy_document" "hcptf_scoped_iam" {
|
|
statement {
|
|
sid = "DenyCreatePolicy"
|
|
effect = "Deny"
|
|
actions = ["iam:CreatePolicy", "iam:CreatePolicyVersion"]
|
|
resources = ["*"]
|
|
}
|
|
statement {
|
|
sid = "CreateExecRoleWithBoundary"
|
|
effect = "Allow"
|
|
actions = ["iam:CreateRole"]
|
|
resources = ["arn:aws:iam::${local.account_id}:role/tf-managed/${local.stack_prefix}*"]
|
|
condition {
|
|
test = "StringLike"
|
|
variable = "iam:PermissionsBoundary"
|
|
values = [
|
|
"arn:aws:iam::${local.account_id}:policy/tf-managed/${local.stack_prefix}*",
|
|
"arn:aws:iam::${local.account_id}:policy/seahaven-lambda-execution-boundary",
|
|
"arn:aws:iam::${local.account_id}:policy/seahaven-lambda-execution-boundary-${local.stack_name}",
|
|
]
|
|
}
|
|
}
|
|
statement {
|
|
sid = "MutateExecRoleWithBoundary"
|
|
effect = "Allow"
|
|
actions = [
|
|
"iam:AttachRolePolicy",
|
|
"iam:PutRolePolicy",
|
|
"iam:PutRolePermissionsBoundary",
|
|
]
|
|
resources = ["arn:aws:iam::${local.account_id}:role/tf-managed/${local.stack_prefix}*"]
|
|
condition {
|
|
test = "StringLike"
|
|
variable = "iam:PermissionsBoundary"
|
|
values = [
|
|
"arn:aws:iam::${local.account_id}:policy/tf-managed/${local.stack_prefix}*",
|
|
"arn:aws:iam::${local.account_id}:policy/seahaven-lambda-execution-boundary",
|
|
"arn:aws:iam::${local.account_id}:policy/seahaven-lambda-execution-boundary-${local.stack_name}",
|
|
]
|
|
}
|
|
}
|
|
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::${local.account_id}:role/tf-managed/${local.stack_prefix}*"]
|
|
}
|
|
statement {
|
|
sid = "PassExecRolesToCompute"
|
|
effect = "Allow"
|
|
actions = ["iam:PassRole"]
|
|
resources = ["arn:aws:iam::${local.account_id}:role/tf-managed/${local.stack_prefix}*"]
|
|
condition {
|
|
test = "StringEquals"
|
|
variable = "iam:PassedToService"
|
|
values = ["lambda.amazonaws.com", "ecs-tasks.amazonaws.com", "scheduler.amazonaws.com"]
|
|
}
|
|
}
|
|
statement {
|
|
sid = "IamReadOnly"
|
|
effect = "Allow"
|
|
actions = [
|
|
"iam:GetPolicy",
|
|
"iam:GetPolicyVersion",
|
|
"iam:GetRole",
|
|
"iam:GetRolePolicy",
|
|
"iam:ListAttachedRolePolicies",
|
|
"iam:ListPolicies",
|
|
"iam:ListPolicyVersions",
|
|
"iam:ListRolePolicies",
|
|
"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::${local.account_id}:role/hcptf-*",
|
|
"arn:aws:iam::${local.account_id}:role/github-cfn-execution-role",
|
|
"arn:aws:iam::${local.account_id}:role/githubdeploy-*",
|
|
"arn:aws:iam::${local.account_id}:role/cdk-hnb659fds-*",
|
|
"arn:aws:iam::${local.account_id}:role/OrganizationAccountAccessRole",
|
|
"arn:aws:iam::${local.account_id}:role/seahaven-*",
|
|
]
|
|
}
|
|
statement {
|
|
sid = "DenyBoundaryTampering"
|
|
effect = "Deny"
|
|
actions = ["iam:DeleteRolePermissionsBoundary", "iam:DeleteUserPermissionsBoundary"]
|
|
resources = [
|
|
"arn:aws:iam::${local.account_id}:role/*",
|
|
"arn:aws:iam::${local.account_id}:user/*",
|
|
]
|
|
}
|
|
statement {
|
|
sid = "DenyBoundaryPolicyEdit"
|
|
effect = "Deny"
|
|
actions = [
|
|
"iam:CreatePolicyVersion",
|
|
"iam:DeletePolicy",
|
|
"iam:DeletePolicyVersion",
|
|
"iam:SetDefaultPolicyVersion",
|
|
]
|
|
resources = ["arn:aws:iam::${local.account_id}:policy/seahaven-*"]
|
|
}
|
|
}
|
|
|
|
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
|
|
# Empty list detaches seahaven-hcptf-iam-management after import.
|
|
managed_policy_arns = []
|
|
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
|
|
managed_policy_arns = ["arn:aws:iam::aws:policy/job-function/ViewOnlyAccess"]
|
|
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
|
|
}
|
|
|
|
# Also import the existing service inline policy (name matches CFN PolicyName)
|
|
# and the plan-refresh sidecar. Copy those documents from
|
|
# lib/terraform-substrate/terraform-substrate.template.yaml. Do not invent a
|
|
# new Get* allow-list.
|
|
#
|
|
# import {
|
|
# to = aws_iam_role.hcptf_apply
|
|
# id = "hcptf-STACK"
|
|
# }
|
|
# import {
|
|
# to = aws_iam_role.hcptf_plan
|
|
# id = "hcptf-STACK-plan"
|
|
# }
|
|
# import {
|
|
# to = aws_iam_role_policy.services
|
|
# id = "hcptf-STACK:STACK-services"
|
|
# }
|
|
# import {
|
|
# to = aws_iam_role_policy.plan_refresh
|
|
# id = "hcptf-STACK-plan:STACK-plan-refresh"
|
|
# }
|