refactor(iam): fold seahaven-site roles into seahaven-hcptf (#175)
Some checks are pending
Deploy / deploy-management (push) Waiting to run
Deploy / deploy-external-dev (push) Waiting to run
Deploy / deploy-security (push) Waiting to run
Deploy / deploy-dev (push) Waiting to run
Deploy / deploy-prod (push) Waiting to run

Prod HCP exec roles for seahaven-site now live in the same stack as payments-dashboard so role ownership is one stack.
This commit is contained in:
Adam Moussa 2026-10-02 17:30:52 +00:00 • committed by GitHub
parent c6de4e0c7f
commit 031e1d1a3b
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
8 changed files with 97 additions and 50 deletions

View file

@ -58,10 +58,10 @@ jobs:
uses: Sea-Haven-Industries/.github/.github/workflows/cd-cdk.yaml@e5691d8a7f96ac4d5a841a82975ff0a4354d53ac # v1.0.7 uses: Sea-Haven-Industries/.github/.github/workflows/cd-cdk.yaml@e5691d8a7f96ac4d5a841a82975ff0a4354d53ac # v1.0.7
with: with:
node-version: "24" node-version: "24"
# seahaven-site-hcptf was imported after the roles left terraform-substrate. # seahaven-hcptf was imported. It owns payments-dashboard and the
# seahaven-hcptf was imported. The deploy creates the three managed # seahaven-site exec roles. Do not create the roles. Deleting the
# policies and removes the inline policies. Do not create the roles. # retired seahaven-site-hcptf stack is a separate prod step.
stacks: "prod-baseline dynamodb-cmk-prod alarm-topic-prod deploy-substrate-prod app-web-acl-prod seahaven-site-hcptf seahaven-hcptf" stacks: "prod-baseline dynamodb-cmk-prod alarm-topic-prod deploy-substrate-prod app-web-acl-prod seahaven-hcptf"
stack-name: "seahaven-prod-baseline" stack-name: "seahaven-prod-baseline"
secrets: secrets:
deploy-role-arn: ${{ secrets.AWS_DEPLOY_ROLE_ARN_PROD }} deploy-role-arn: ${{ secrets.AWS_DEPLOY_ROLE_ARN_PROD }}

View file

@ -33,8 +33,7 @@ are noted):
| `seahaven-engineering-access` | 328440206208 | us-east-1 | Identity Center group `engineering`. `EngineeringProd` reads payments-dashboard configuration and seahaven-site in 011934824531. `EngineeringDev` has no allow (PLAT-235, PLAT-236). | | `seahaven-engineering-access` | 328440206208 | us-east-1 | Identity Center group `engineering`. `EngineeringProd` reads payments-dashboard configuration and seahaven-site in 011934824531. `EngineeringDev` has no allow (PLAT-235, PLAT-236). |
| `seahaven-external-dev-baseline` | 396287094661 | us-east-1 | Member-account baseline: Config, GuardDuty, Security Hub (FSBP + CIS v3.0), Access Analyzer, flow logs, budget | | `seahaven-external-dev-baseline` | 396287094661 | us-east-1 | Member-account baseline: Config, GuardDuty, Security Hub (FSBP + CIS v3.0), Access Analyzer, flow logs, budget |
| `seahaven-terraform-substrate` | 011934824531, 710827005802 | us-east-1 | Removed from the CDK app and from CD (PLAT-147). Live stacks remain until `scripts/delete-terraform-substrate-prod-dev.sh`. The six imported prod pairs are already forgotten. | | `seahaven-terraform-substrate` | 011934824531, 710827005802 | us-east-1 | Removed from the CDK app and from CD (PLAT-147). Live stacks remain until `scripts/delete-terraform-substrate-prod-dev.sh`. The six imported prod pairs are already forgotten. |
| `seahaven-site-hcptf` | 011934824531 | us-east-1 | Imported `hcptf-seahaven-site` apply and plan roles (PLAT-225). Policies are managed at `/tf-managed/`. On the prod deploy job. Do not create the roles. | | `seahaven-hcptf` | 011934824531, 710827005802 | us-east-1 | payments-dashboard HCP apply and plan roles, scoped policies, and the Lambda boundary in prod and dev. Prod also owns the imported `hcptf-seahaven-site` apply and plan roles (PLAT-225), with policies at `/tf-managed/`. Trust is pinned per workspace. On the dev and prod deploy jobs. Do not create the roles. |
| `seahaven-hcptf` | 011934824531, 710827005802 | us-east-1 | payments-dashboard HCP apply and plan roles, scoped policies, and the Lambda boundary. Trust is pinned to `payments-dashboard-prod` in project `seahaven-prod`, and to `payments-dashboard-dev` in project `seahaven-dev`. Roles and boundary are imported. On the dev and prod deploy jobs. |
| `seahaven-terraform-substrate` | 396287094661 | us-east-1 | Staged manually for SHOC backend/frontend adoption; exact HCP roles and deploy boundaries referencing the existing OIDC provider. Stays (PLAT-148). | | `seahaven-terraform-substrate` | 396287094661 | us-east-1 | Staged manually for SHOC backend/frontend adoption; exact HCP roles and deploy boundaries referencing the existing OIDC provider. Stays (PLAT-148). |
| `seahaven-security-baseline` | 001520130573 | us-east-1 | Member-account baseline for the delegated security-admin account (same construct set) | | `seahaven-security-baseline` | 001520130573 | us-east-1 | Member-account baseline for the delegated security-admin account (same construct set) |
| `seahaven-dev-baseline` | 710827005802 | us-east-1 | Member-account baseline for internal dev/staging (org-managed detection — no local GuardDuty/SecurityHub) | | `seahaven-dev-baseline` | 710827005802 | us-east-1 | Member-account baseline for internal dev/staging (org-managed detection — no local GuardDuty/SecurityHub) |
@ -78,7 +77,6 @@ the TypeScript source — no separate compile step needed for `cdk synth` /
| `dynamodb-cmk-prod` | `seahaven-dynamodb-cmk` | 011934824531 | us-east-1 | `lib/dynamodb-cmk-stack.ts` | | `dynamodb-cmk-prod` | `seahaven-dynamodb-cmk` | 011934824531 | us-east-1 | `lib/dynamodb-cmk-stack.ts` |
| `alarm-topic-prod` | `seahaven-alarm-topic` | 011934824531 | us-east-1 | `lib/alarm-topic-stack.ts` | | `alarm-topic-prod` | `seahaven-alarm-topic` | 011934824531 | us-east-1 | `lib/alarm-topic-stack.ts` |
| `app-web-acl-prod` | `seahaven-app-web-acl` | 011934824531 | us-east-1 | `lib/app-web-acl-stack.ts` | | `app-web-acl-prod` | `seahaven-app-web-acl` | 011934824531 | us-east-1 | `lib/app-web-acl-stack.ts` |
| `seahaven-site-hcptf` | `seahaven-site-hcptf` | 011934824531 | us-east-1 | `lib/seahaven-site-hcptf-stack.ts` |
| `seahaven-hcptf` | `seahaven-hcptf` | 011934824531 | us-east-1 | `lib/seahaven-hcptf-stack.ts` | | `seahaven-hcptf` | `seahaven-hcptf` | 011934824531 | us-east-1 | `lib/seahaven-hcptf-stack.ts` |
| `seahaven-hcptf-dev` | `seahaven-hcptf` | 710827005802 | us-east-1 | `lib/seahaven-hcptf-stack.ts` | | `seahaven-hcptf-dev` | `seahaven-hcptf` | 710827005802 | us-east-1 | `lib/seahaven-hcptf-stack.ts` |
@ -240,12 +238,13 @@ Console / one-shot CLI owns only:
Do not manage prod/dev workload IAM (`hcptf-<stack>` pairs, Lambda exec Do not manage prod/dev workload IAM (`hcptf-<stack>` pairs, Lambda exec
roles, `policy/tf-managed/<stack>` ceilings) in the console. Do not append roles, `policy/tf-managed/<stack>` ceilings) in the console. Do not append
new prod/dev `hcptf-<stack>` pairs to this template. New prod/dev HCP stacks new prod/dev `hcptf-<stack>` pairs to this template. New prod/dev HCP stacks
do not need an org-baseline IAM PR. payments-dashboard HCP roles and do not need an org-baseline IAM PR. Prod HCP exec roles for
the Lambda boundary are stack `seahaven-hcptf` in prod and dev. Both copies payments-dashboard and seahaven-site both live in `seahaven-hcptf`.
are imported and are on the deploy jobs. The deploy creates Dev `seahaven-hcptf` is payments-dashboard only. Both account copies
are imported and are on the deploy jobs. The payments deploy creates
`payments-dashboard-hcptf-iam`, `payments-dashboard-hcptf-services`, and `payments-dashboard-hcptf-iam`, `payments-dashboard-hcptf-services`, and
`payments-dashboard-hcptf-plan`, and removes the inline policies. Do not `payments-dashboard-hcptf-plan`, and removes the inline policies. Do not
create the roles or the boundary. create the roles, the boundary, or the seahaven-site roles.
**External-dev IAM stays in this repo (PLAT-148).** SHOC backend/frontend HCP **External-dev IAM stays in this repo (PLAT-148).** SHOC backend/frontend HCP
roles, SHOC deploy/runtime boundaries, `shoc-frontend-resources.ts`, and roles, SHOC deploy/runtime boundaries, `shoc-frontend-resources.ts`, and
@ -267,7 +266,7 @@ Until the delete script runs, the live stacks still hold:
`seahaven-lambda-execution-boundary-*` allow-list; frozen, do not append). `seahaven-lambda-execution-boundary-*` allow-list; frozen, do not append).
The six imported prod pairs are Retain-removed. `seahaven-site` is The six imported prod pairs are Retain-removed. `seahaven-site` is
`seahaven-site-hcptf`. `sh-openswe-traces` is gone. `seahaven-hcptf`. `sh-openswe-traces` is gone.
External-dev still carries: External-dev still carries:
@ -314,7 +313,7 @@ and nonprod (PLAT-145), covering `hcptf-bootstrap*` plus the break-glass /
CDK / `githubdeploy-*` set already on the security OU. CDK / `githubdeploy-*` set already on the security OU.
The six live prod pairs are imported into the owning app, not recreated, then The six live prod pairs are imported into the owning app, not recreated, then
Retain-removed from this template. `seahaven-site` is `seahaven-site-hcptf`. Retain-removed from this template. `seahaven-site` is `seahaven-hcptf`.
`sh-openswe-traces` is not imported. The prod/dev stacks are deleted after `sh-openswe-traces` is not imported. The prod/dev stacks are deleted after
that forget. See the first-apply and import runbooks below. that forget. See the first-apply and import runbooks below.
@ -611,7 +610,7 @@ wildcards. Do not enumerate provider Get* APIs.
state: `afi-backup-monitor`, `front-integrations`, `paychex-integrations`, state: `afi-backup-monitor`, `front-integrations`, `paychex-integrations`,
`procurement-ingest`, `meal-order-manager`, `seahaven-door-unlock-api`. `procurement-ingest`, `meal-order-manager`, `seahaven-door-unlock-api`.
`sh-openswe-traces` was decommissioned (PLAT-196) and is not imported. `sh-openswe-traces` was decommissioned (PLAT-196) and is not imported.
`seahaven-site` is stack `seahaven-site-hcptf` (PLAT-225), not this template. `seahaven-site` is stack `seahaven-hcptf` (PLAT-225), not this template.
Repos that do not change: SHOC (`shoc-backend`, `shoc-frontend-new`), Repos that do not change: SHOC (`shoc-backend`, `shoc-frontend-new`),
remaining SAM / unmigrated stacks. remaining SAM / unmigrated stacks.

View file

@ -11,7 +11,6 @@ import { TerraformSubstrateStack } from "../lib/terraform-substrate-stack";
import { DynamoDbCmkStack } from "../lib/dynamodb-cmk-stack"; import { DynamoDbCmkStack } from "../lib/dynamodb-cmk-stack";
import { AppWebAclStack } from "../lib/app-web-acl-stack"; import { AppWebAclStack } from "../lib/app-web-acl-stack";
import { SeahavenHcptfStack } from "../lib/seahaven-hcptf-stack"; import { SeahavenHcptfStack } from "../lib/seahaven-hcptf-stack";
import { SeahavenSiteHcptfStack } from "../lib/seahaven-site-hcptf-stack";
import { MemberBaselineStack } from "../lib/member-baseline-stack"; import { MemberBaselineStack } from "../lib/member-baseline-stack";
import { OrgGovernanceStack } from "../lib/org-governance-stack"; import { OrgGovernanceStack } from "../lib/org-governance-stack";
import { PlatformAccessStack } from "../lib/platform-access-stack"; import { PlatformAccessStack } from "../lib/platform-access-stack";
@ -231,19 +230,12 @@ new AppWebAclStack(app, "app-web-acl-prod", {
env: { account: PROD_ACCOUNT, region: "us-east-1" }, env: { account: PROD_ACCOUNT, region: "us-east-1" },
}); });
// seahaven-site exec roles (PLAT-225). Not part of terraform-substrate.
// Imported. On the prod deploy job. A create fails because the roles already exist.
// Inline policies are managed policies at /tf-managed/. Do not recreate the roles.
new SeahavenSiteHcptfStack(app, "seahaven-site-hcptf", {
stackName: "seahaven-site-hcptf",
env: { account: PROD_ACCOUNT, region: "us-east-1" },
});
// payments-dashboard HCP roles, scoped policies, and the Lambda boundary. // payments-dashboard HCP roles, scoped policies, and the Lambda boundary.
// The same three names already exist in prod and dev. Import them with // Prod also includes the imported seahaven-site apply and plan roles
// `-c hcptfPaymentsImport=true`. A create fails. Neither stack is on a // (PLAT-225). A create fails. Import site with `-c hcptfSiteImport=true`.
// deploy job until that import succeeds. Trust is pinned per workspace. // Trust is pinned per workspace.
const hcptfPaymentsImport = contextBoolean("hcptfPaymentsImport"); const hcptfPaymentsImport = contextBoolean("hcptfPaymentsImport");
const hcptfSiteImport = contextBoolean("hcptfSiteImport");
const paymentsSecrets = ( const paymentsSecrets = (
account: string, account: string,
suffixes: readonly string[], suffixes: readonly string[],
@ -269,6 +261,8 @@ new SeahavenHcptfStack(app, "seahaven-hcptf", {
"expense-slack-signing-secret-lbb78J", "expense-slack-signing-secret-lbb78J",
]), ]),
importExisting: hcptfPaymentsImport, importExisting: hcptfPaymentsImport,
includeSeahavenSite: true,
siteImportExisting: hcptfSiteImport,
}); });
new SeahavenHcptfStack(app, "seahaven-hcptf-dev", { new SeahavenHcptfStack(app, "seahaven-hcptf-dev", {

View file

@ -0,0 +1,17 @@
{
"SeahavenSiteApplyRoleE31E1F61": {
"RoleName": "hcptf-seahaven-site"
},
"SeahavenSitePlanRoleF0DFA964": {
"RoleName": "hcptf-seahaven-site-plan"
},
"SeahavenSiteIamPolicy1032B47C": {
"PolicyArn": "arn:aws:iam::011934824531:policy/tf-managed/seahaven-site-hcptf-iam"
},
"SeahavenSiteServicesPolicy1E92BC24": {
"PolicyArn": "arn:aws:iam::011934824531:policy/tf-managed/seahaven-site-hcptf-services"
},
"SeahavenSitePlanPolicyC4736E1F": {
"PolicyArn": "arn:aws:iam::011934824531:policy/tf-managed/seahaven-site-hcptf-plan"
}
}

View file

@ -8,7 +8,8 @@ const MAX_POLICY_CHARS = 6144;
/** /**
* Fails synth when an HCP stack's managed policy document reaches the IAM * Fails synth when an HCP stack's managed policy document reaches the IAM
* size quota, or when a role in that stack carries an inline policy. * size quota, or when a role in that stack carries an inline policy.
* Register it on seahaven-hcptf and seahaven-site-hcptf only. * Register it on seahaven-hcptf only. That stack owns payments-dashboard
* in prod and dev, and seahaven-site in prod.
* *
* `allowInlinePolicies` is only for the one-time `cdk import` template. * `allowInlinePolicies` is only for the one-time `cdk import` template.
* That template has to name the live inline policies so the following * That template has to name the live inline policies so the following

View file

@ -2,11 +2,15 @@ import * as cdk from "aws-cdk-lib";
import * as iam from "aws-cdk-lib/aws-iam"; import * as iam from "aws-cdk-lib/aws-iam";
import { Construct } from "constructs"; import { Construct } from "constructs";
import { HcptfPolicyAspect } from "./hcptf-policy-aspect"; import { HcptfPolicyAspect } from "./hcptf-policy-aspect";
import { SeahavenSiteRoles } from "./seahaven-site-hcptf-stack";
/** /**
* payments-dashboard HCP exec roles. One stack per account. Prod is * HCP exec roles. One stack per account. Prod is 011934824531 and also
* 011934824531, workspace payments-dashboard-prod, project seahaven-prod. * hosts the seahaven-site apply and plan roles. Dev is 710827005802 and
* Dev is 710827005802, workspace payments-dashboard-dev, project seahaven-dev. * stays payments-dashboard only.
*
* payments-dashboard: workspace payments-dashboard-prod, project
* seahaven-prod, or workspace payments-dashboard-dev, project seahaven-dev.
* *
* hcptf-payments-dashboard, hcptf-payments-dashboard-plan, and * hcptf-payments-dashboard, hcptf-payments-dashboard-plan, and
* payments-dashboard-lambda-boundary already existed in both accounts and * payments-dashboard-lambda-boundary already existed in both accounts and
@ -37,12 +41,27 @@ export interface SeahavenHcptfStackProps extends cdk.StackProps {
* Default is the managed-policy template. * Default is the managed-policy template.
*/ */
importExisting?: boolean; importExisting?: boolean;
/** Prod only. Fold seahaven-site exec roles into this stack. */
includeSeahavenSite?: boolean;
/**
* Synthesize the seahaven-site import template. Set from
* `-c hcptfSiteImport=true`. Omits site role tags and site outputs.
*/
siteImportExisting?: boolean;
} }
export class SeahavenHcptfStack extends cdk.Stack { export class SeahavenHcptfStack extends cdk.Stack {
constructor(scope: Construct, id: string, props: SeahavenHcptfStackProps) { constructor(scope: Construct, id: string, props: SeahavenHcptfStackProps) {
super(scope, id, props); super(scope, id, props);
if (props.importExisting && props.siteImportExisting) {
throw new Error("hcptfPaymentsImport and hcptfSiteImport cannot both be set");
}
paymentsDashboard(this, props); paymentsDashboard(this, props);
if (props.includeSeahavenSite) {
new SeahavenSiteRoles(this, "SeahavenSite", {
importExisting: props.siteImportExisting === true,
});
}
cdk.Aspects.of(this).add(new HcptfPolicyAspect(props.importExisting === true)); cdk.Aspects.of(this).add(new HcptfPolicyAspect(props.importExisting === true));
} }
} }

View file

@ -1,25 +1,43 @@
import * as cdk from "aws-cdk-lib"; import * as cdk from "aws-cdk-lib";
import * as iam from "aws-cdk-lib/aws-iam"; import * as iam from "aws-cdk-lib/aws-iam";
import { Construct } from "constructs"; import { Construct } from "constructs";
import { HcptfPolicyAspect } from "./hcptf-policy-aspect";
/** /**
* Prod exec roles for the seahaven-site HCP workspace (PLAT-225). * Prod exec roles for the seahaven-site HCP workspace (PLAT-225).
* *
* These roles already exist and were imported into this stack. Do not * Nested in the prod seahaven-hcptf stack. These roles already exist.
* create them. A plain create fails because the roles already exist. * Do not create them. A plain create fails because the roles already exist.
* The stack is on the prod deploy job.
* *
* Import identifiers were the role names `hcptf-seahaven-site` and * Import identifiers are the role names `hcptf-seahaven-site` and
* `hcptf-seahaven-site-plan`. Construct ids stay ApplyRole and PlanRole. * `hcptf-seahaven-site-plan`, plus the three /tf-managed/ policy ARNs.
* Construct ids stay ApplyRole and PlanRole under SeahavenSite.
* Inline policies are managed policies at /tf-managed/. Do not rename * Inline policies are managed policies at /tf-managed/. Do not rename
* the roles. * the roles.
*
* `importExisting` is the `-c hcptfSiteImport=true` template. It omits
* role tags and the role ARN outputs. CloudFormation rejects both on an
* IAM role import. The steady-state template adds them back.
*/ */
export class SeahavenSiteHcptfStack extends cdk.Stack { export interface SeahavenSiteRolesProps {
constructor(scope: Construct, id: string, props: cdk.StackProps) { /** Omit role tags and outputs. Set from `-c hcptfSiteImport=true`. */
super(scope, id, props); importExisting?: boolean;
}
const account = this.account; export class SeahavenSiteRoles extends Construct {
constructor(scope: Construct, id: string, props: SeahavenSiteRolesProps = {}) {
super(scope, id);
const importing = props.importExisting === true;
// Overrides the parent stack's Project=payments-dashboard tag.
cdk.Tags.of(this).add("Project", "seahaven-site", { priority: 200 });
if (importing) {
const roleOnly = { priority: 300, includeResourceTypes: ["AWS::IAM::Role"] };
cdk.Tags.of(this).remove("Project", roleOnly);
cdk.Tags.of(this).remove("Owner", roleOnly);
cdk.Tags.of(this).remove("ManagedBy", roleOnly);
}
const account = cdk.Stack.of(this).account;
const deployRole = `arn:aws:iam::${account}:role/tf-managed/githubdeploy-seahaven-site`; const deployRole = `arn:aws:iam::${account}:role/tf-managed/githubdeploy-seahaven-site`;
const boundary = `arn:aws:iam::${account}:policy/tf-managed/seahaven-site-githubdeploy-boundary`; const boundary = `arn:aws:iam::${account}:policy/tf-managed/seahaven-site-githubdeploy-boundary`;
const bucket = "arn:aws:s3:::seahaven-site-prod"; const bucket = "arn:aws:s3:::seahaven-site-prod";
@ -62,7 +80,7 @@ export class SeahavenSiteHcptfStack extends cdk.Stack {
maxSessionDuration: 3600, maxSessionDuration: 3600,
assumeRolePolicyDocument: trust(hcpOidc, "apply"), assumeRolePolicyDocument: trust(hcpOidc, "apply"),
managedPolicyArns: [iamPolicy.ref, services.ref], managedPolicyArns: [iamPolicy.ref, services.ref],
tags: roleTags(), ...(importing ? {} : { tags: roleTags() }),
}); });
retain(apply); retain(apply);
@ -74,17 +92,16 @@ export class SeahavenSiteHcptfStack extends cdk.Stack {
"arn:aws:iam::aws:policy/job-function/ViewOnlyAccess", "arn:aws:iam::aws:policy/job-function/ViewOnlyAccess",
planRefresh.ref, planRefresh.ref,
], ],
tags: roleTags(), ...(importing ? {} : { tags: roleTags() }),
}); });
retain(plan); retain(plan);
new cdk.CfnOutput(this, "ApplyRoleArn", { value: apply.attrArn }); if (!importing) {
new cdk.CfnOutput(this, "PlanRoleArn", { value: plan.attrArn }); const applyArn = new cdk.CfnOutput(this, "ApplyRoleArn", { value: apply.attrArn });
const planArn = new cdk.CfnOutput(this, "PlanRoleArn", { value: plan.attrArn });
cdk.Tags.of(this).add("Project", "seahaven-site"); applyArn.overrideLogicalId("SeahavenSiteApplyRoleArn");
cdk.Tags.of(this).add("Owner", "adam@seahavenind.com"); planArn.overrideLogicalId("SeahavenSitePlanRoleArn");
cdk.Tags.of(this).add("ManagedBy", "cdk"); }
cdk.Aspects.of(this).add(new HcptfPolicyAspect());
} }
} }

View file

@ -78,7 +78,7 @@ Description: >-
# those roles (PLAT-144/PLAT-146). The six imported prod pairs are removed # those roles (PLAT-144/PLAT-146). The six imported prod pairs are removed
# here. Their previous DeletionPolicy is Retain, so CloudFormation forgets # here. Their previous DeletionPolicy is Retain, so CloudFormation forgets
# them (PLAT-147). hcptf-sh-openswe-traces was already forgotten. # them (PLAT-147). hcptf-sh-openswe-traces was already forgotten.
# seahaven-site lives in seahaven-site-hcptf. External-dev # seahaven-site lives in seahaven-hcptf. External-dev
# SHOC roles below remain in this template (PLAT-148). All remaining subs are # SHOC roles below remain in this template (PLAT-148). All remaining subs are
# exact StringEquals (never StringLike, never a wildcarded run_phase). # exact StringEquals (never StringLike, never a wildcarded run_phase).
# #