# cp9 AWS substrate

This is the temporary in-repo home for the cp9 Terraform substrate used by the Dev full persona. The intended durable home is `tidbcloud/auto-deploy/terraform/aws/cp9-substrate`.

cp9 copies this root into `.cp9/<name>/terraform/source`, copies the persona-owned HCL var file into `.cp9/<name>/terraform/<persona-name-id>.tfvars`, injects runtime values into that session-local copy, and runs Terraform from the copied source directory.

## Scope

This module may create only per-session AWS infrastructure for a selected cp9 Dev full session:

- a tagged workload IAM role/policy for cp9-rendered Crossplane/provider/dataplane-manager/cluster-ng-server credentials,
- for `remote-ec2-k3d`, a tagged runtime role and EC2 Instance Profile that use the existing `cp9-developer-power` policy under the existing developer boundary,
- a tagged session-scoped short-lived ACM PCA shared by every product line,
- a tagged session-scoped KMS key for Premium and Dedicated Dataflow encryption when Dedicated Crossplane is enabled,
- tagged Secrets Manager secret references for metadb credentials,
- the session-prefixed Starter regional EKS cluster, VPC, three public-routed
  subnets, seven fixed-role managed node groups, per-group launch templates, EKS
  add-ons, and runtime IAM roles when Starter activation is enabled; the
  subnets carry the product-required `type=private` discovery tag even though
  the short-lived cp9 route table remains public,
- a separately configured initial/minimum TiDB Pod target applied only to the
  product-policy-selected Starter serving lane; this is independent of the
  physical TiDB serving node-group count,
- Starter session storage, queues, repositories, and the private hosted-zone
  inputs consumed by that regional runtime,
- outputs that cp9 can later materialize into app-delivery/Tilt patch inputs.

It must not create shared or product-owned infrastructure such as:

- a shared/staging control-plane EKS cluster,
- Premium or Dedicated customer/dataplane EKS clusters,
- ArgoCD registration,
- app-delivery workflow dispatch resources,
- global region registration,
- shared staging/prod Terraform state dependencies.

## Local session AWS profile

Daily cp9 users should not run an extra profile setup script. `cp9 setup`
writes or replaces the Session-named local runtime profile immediately after the
source-profile and tagged `cp9-developer` role chain passes validation.
`cp9 up` performs the same idempotent write during a fresh `iam_ready` and
restores it before resuming a lifecycle whose `iam_ready` already completed, so
Terraform or host creation cannot inherit another session's runtime binding.
The source profile must be an existing bootstrap profile distinct from the
cp9-managed runtime profile.

The Terraform provider uses the runtime `aws_profile` (the top-level Session
name). Its credential helper uses `aws_source_profile` to assume the
admin-managed `cp9-developer` role with `cp9-owner` and `cp9-session` session
tags. The same runtime profile is available before Terraform starts for cp9
and day-2 commands.

The written profile looks like:

```ini
[profile cp9-local]
# cp9-session = cp9-local
# cp9-owner = alice@example.com
region = us-west-2
output = yaml
credential_process = /bin/bash <aws-config-dir>/cp9/credential-process/cp9-credential-process.sh <source-profile> arn:aws:iam::<runtime-account-id>:role/cp9-developer cp9-local 3600 alice@example.com
```

The credential helper has a stable path outside the disposable session
directory. CP9 serializes setup/`iam_ready` profile writes and destroy-time
cleanup with the same host-local lock. A write atomically replaces an older
section with the values derived from the current `cp9.yaml`, creates a
permission-preserving backup of an existing config, and leaves unrelated
profiles untouched. `cp9 destroy` removes this profile section only while its
`cp9-session` marker still names the session being destroyed; a missing marker,
a concurrent update, or a newer session binding is retained. The shared helper
itself is not session-deleted.

Expected verification ARN shape after setup:

```text
arn:aws:sts::<runtime-account-id>:assumed-role/cp9-developer/<owner>
```

For ad-hoc commands, `cp9-session` defaults to `cp9-local`. A caller may override it with `CP9_SESSION_NAME`:

```sh
CP9_SESSION_NAME=cp9-yifan-rs1 aws sts get-caller-identity --profile cp9-yifan-rs1
```

The helper lowercases the owner derived from the SSO session name. `CP9_OWNER` and `CP9_ROLE_SESSION_NAME` can override the derived values when needed for CI or debugging.

## Persona var file contract

Persona owners provide one HCL var file named `<persona-name-id>.tfvars`, for example `premium.tfvars`. The file is reviewed like normal Terraform input, not as a cp9-specific YAML layer.

Stable persona-owned fields:

| Field | Purpose |
|---|---|
| `persona` | Business/persona key. This temporary root accepts the `premium` family. The deprecated `premium-reserved` compatibility file has been removed. |
| `aws_assume_role_duration` | Deprecated compatibility input; the runtime profile helper owns its one-hour role session. |
| `tags` | Static tags such as `cp9-persona`; cp9 merges runtime tags into the session copy. |
| `iam_enabled` | Create the per-session workload IAM role/policy. |
| `developer_role_arn` | Shared `cp9-developer` role contract. |
| `developer_boundary_policy_arn` | Boundary applied to the workload role and checked against `cp9-developer`. |
| `developer_power_policy_arn` | Existing shared policy attached to the remote EC2 runtime role; omitted for local k3d. |
| `remote_ec2_k3d_enabled` | Create the session-owned remote runtime role and Instance Profile. cp9 injects this from the selected host mode. |
| `starter_node_boundary_policy_arn` | Optional override for Starter remote EKS role boundaries. Empty uses `developer_boundary_policy_arn`, matching the live `cp9-developer` bootstrap. |
| `dataplane_role_name` / `dataplane_accounts` | Explicit dataplane assume-role target set. |
| `billing_sqs_arn` | Optional billing queue send target. Empty means no billing SQS permission. |
| `route53_enabled` | Optional DNS permissions. Enabled for the full Dev full proof path. |
| `metadb_*` | Secret/output inputs for the metadb handoff. Terraform creates the secret reference, not the database. |
| `provider_credential_secret_names` | Optional future handoff map for app-delivery patches. |

Runtime fields cp9 injects into the session-local copy:

| Field | Source |
|---|---|
| `session_name` | Top-level `name` in `cp9.yaml`. |
| `owner` | Resolved AWS/CI owner. |
| `target_cloud` / `target_region` / `target_account` | Resolved session target and IAM readiness result. |
| `aws_profile` / `aws_source_profile` | Persisted IAM-ready profile pair from `cp9 setup` / `iam_ready`. |
| `tags.cp9-owner` / `tags.cp9-session` / `tags.cp9-managed` / `tags.cp9-ttl` | Cleanup and ABAC tags. These override same-named static tags in the session copy. |

## Manual bootstrap

Run from this directory only for substrate development. Real `cp9 up` starts from the persona-owned var file named `<persona-name-id>.tfvars`, then mutates only the session-local copy under `.cp9/<name>/terraform/`.

The committed persona var file owns stable persona choices, such as IAM feature toggles, metadb inputs, dataplane account targets, and static tags like `cp9-persona`. cp9 injects runtime fields into the copied file:

- `session_name`
- `owner`
- `target_cloud`
- `target_region`
- `target_account`
- `aws_profile`
- `aws_source_profile`
- additional `tags`, including `cp9-owner`, `cp9-session`, `cp9-managed`, and `cp9-ttl`

cp9 does not edit the committed source var file.

```sh
terraform init
terraform validate
cp premium.tfvars /tmp/premium.tfvars
# Edit /tmp/premium.tfvars to add the same runtime fields cp9 injects:
# session_name, owner, target_cloud, target_region, target_account,
# aws_profile, aws_source_profile, and merged tags.
terraform plan -var-file=/tmp/premium.tfvars
```

The source AWS profile and configured role resolve one effective account. cp9
persists that account from IAM readiness as `target_account`; Terraform uses
the session runtime profile and independently requires its active caller
identity to equal the persisted value before mutation. The provider runs as
the assumed `cp9-developer` role.
Neither `terraform plan` nor `terraform apply` writes `~/.aws/config`; profile
ownership is established by `cp9 setup`/`iam_ready`.

The durable `cp9-developer` role, attached policies, permission boundaries, and
shared cp9 DNS root are managed by admins via
[`../../cp9-developer/SKILL.md`](../../cp9-developer/SKILL.md).

## Handoff outputs

- `remote_ec2_runtime`: typed remote runtime role, Instance Profile, shared policy, and boundary contract. Consumers must use this output rather than derive IAM names.
- `iam.role_arn` / `iam.role_name`: typed IAM output parsed by cp9.
- `metadb.endpoint` / `metadb.port` / `metadb.database` / `metadb.user` / `metadb.secret_ref`: typed metadb output parsed by cp9.
- `session_cmk.arn` / `session_cmk.region`: the session's KMS key. Global Control encrypts control plane secrets with it and Regional Control and the data plane decrypt them, so every service holding a `cmkArn` receives this one key. Always created.
- `session_product_pca.arn` / `session_product_pca.region`: typed short-lived ACM PCA output shared by every product line in the session.
- `runtime_patch_inputs`: compact object for future `.cp9/<name>/tilt/inputs` materialization.

Every taggable resource receives:

- `cp9-owner`
- `cp9-session`
- `cp9-managed = terraform-substrate`
- `cp9-ttl`

The managed-node-group tag alone does not propagate to its AWS-managed child
resources. CP9 therefore applies the same cleanup tags at EC2 instance, root
volume, and network-interface creation through each group launch template, and
also tags the generated Auto Scaling group with propagation enabled. The
Starter EBS CSI StorageClasses receive the session owner/session/TTL tags before
they create product-runtime data volumes.
