plugins/lisa-cdk-copilot/skills/setup-aws-accounts/SKILL.md
Provision the multi-account AWS environment a Lisa CDK project deploys into, using the @codyswann/aws-soc2-setup CLI (AWS Organizations, Control Tower, IAM Identity Center, SOC 2 controls). Use when a CDK project still has PLACEHOLDER account IDs in config/environments.ts, when asked to create the dev/staging/production/shared AWS accounts, or when CDK deploys fail because accounts, SSO profiles, or bootstrap are missing. Drives the CLI for the org + OU + security foundation and Account Factory account vending, writes the AWS SSO profiles the project's scripts expect, runs cdk bootstrap with cross-account pipeline trust, replaces PLACEHOLDER account IDs, sets the AWS_ACCOUNT_ID_* GitHub secrets when CI uses them, and records the account map in .lisa.config.json. Idempotent — safe to re-run. Console-only prerequisites (root MFA, enabling Identity Center, creating the Control Tower landing zone) are surfaced as an explicit human checklist, never automated.
npx skillsauth add codyswanngt/lisa setup-aws-accountsInstall this skill globally with one command. Works with Claude Code, Cursor, and Windsurf.
3 of 9 scanners reported clean
Some scanners were skipped, did not run, or reported a non-clean status. Review each row below.
Provision and wire the AWS accounts a CDK project deploys into. The account
supply side runs through the @codyswann/aws-soc2-setup
CLI (AWS Organizations, Control Tower, IAM Identity Center, security services,
SOC 2 controls); the demand side — profiles, cdk bootstrap, config files, CI
secrets — is wired by this skill so the project goes from PLACEHOLDER account
IDs to deployable in one flow.
config/environments.ts still has accountId: "PLACEHOLDER"
entries, so cdk deploy silently skips those environments.scripts/pre-deployment-checklist.sh fails because <prefix>-<env> AWS
profiles don't exist or don't authenticate.Not for: deploying stacks (that's cdk deploy / CI), or GitHub repo governance
(that's lisa-setup-github-repo).
npx -y @codyswann/aws-soc2-setup@1 <command>. Pin at least the
major version — an unpinned npx pull is a supply-chain risk for a tool that
runs with management-account credentials. Always set NO_COLOR=1 so output
parses cleanly.--json output. Drive it for side
effects, then read authoritative state back with the plain aws CLI
(aws organizations list-accounts, aws sts get-caller-identity). Never
trust scraped text alone for account IDs.setup is safe — except controltower provision-account,
which is not deduped by name. Always check aws organizations list-accounts
before provisioning and skip accounts that already exist.--dry-run previews every mutating command; status and whoami are
read-only.controltower provision-account -o takes the OU name (e.g. Workloads);
register-ou and enable-controls take the OU id/ARN. Don't mix them up.NO_COLOR=1 npx -y @codyswann/aws-soc2-setup@1 whoami -p <management-profile> -r <region>
NO_COLOR=1 npx -y @codyswann/aws-soc2-setup@1 status -p <management-profile> -r <region>
status reports five independent checks: credentials, Organizations,
recommended OUs, IAM Identity Center, member accounts.
The human gate. Three prerequisites are console-only and must never be
automated. If status shows Identity Center or the landing zone missing, stop
and present this checklist in plain language a non-technical operator can
follow, then end the run:
aws configure sso (interactive).Re-run this skill after those are done — Step 1 will pass and the flow continues from wherever it left off.
Collect (via arguments if provided, AskUserQuestion otherwise; use stated
defaults when running unattended):
| Input | Default | Notes |
| --- | --- | --- |
| Management profile | AWS_PROFILE | Must be the org management account |
| Region | us-east-1 | Match the project's config/environments.ts |
| Profile prefix | project name from package.json | Matches AWS_PROFILE_PREFIX used by scripts/pre-deployment-checklist.sh |
| Environments | dev, staging, production, shared | shared hosts the pipeline and gets cross-account trust |
| Root email pattern | ask — no safe default | Each account needs a unique root email; suggest plus-addressing: aws+<prefix>-<env>@<domain> |
| Identity Center username | ask — no safe default | The Identity Store user that gets AWSAdministratorAccess on each new account in Step 5; verify it exists (create with sso create-user if not) |
| Identity Center start URL | from status output or the console | The full sso_start_url for the sso-session block in Step 5 (default form https://<domain>.awsapps.com/start, but custom domains differ — never reconstruct it from parts) |
Show the plan, get explicit approval, then apply:
NO_COLOR=1 npx -y @codyswann/aws-soc2-setup@1 setup --dry-run -p <mgmt> -r <region>
Present the dry-run output and stop for confirmation before mutating the
organization — AskUserQuestion when interactive, or an unambiguous
apply/confirm argument from the caller when running unattended. Silence is not
approval. Only then:
NO_COLOR=1 npx -y @codyswann/aws-soc2-setup@1 setup -p <mgmt> -r <region> \
[--ou <workloads-ou-id>] [--central-account <id>] [--admin-account <id>] [--audit-account <id>]
This ensures the organization, creates the Infrastructure/Workloads/Sandbox
OUs, enables the security services (GuardDuty, Security Hub, Config, …),
conformance packs, and — when --ou is passed — registers the OU with Control
Tower and enables the SOC 2 control baseline. Safe to re-run.
For each environment, first check it doesn't already exist:
aws organizations list-accounts --profile <mgmt> \
--query "Accounts[?Name=='<prefix>-<env>'].{Id:Id,Name:Name,State:State}" --output table
(Use State, not Status — AWS is retiring the Status field in favor of
State.)
Then provision the missing ones, one at a time, each with a unique root email.
Stage accounts go into the Workloads OU:
NO_COLOR=1 npx -y @codyswann/aws-soc2-setup@1 controltower provision-account \
-p <mgmt> -r <region> \
-n <prefix>-<env> -e aws+<prefix>-<env>@<domain> -o Workloads --wait
The shared (pipeline) account goes into Infrastructure instead:
NO_COLOR=1 npx -y @codyswann/aws-soc2-setup@1 controltower provision-account \
-p <mgmt> -r <region> \
-n <prefix>-shared -e aws+<prefix>-shared@<domain> -o Infrastructure --wait
--wait polls Account Factory (~minutes per account) and prints
New account ID: <id>. After all accounts finish, harvest the authoritative
env → account-ID map from aws organizations list-accounts — that map drives
every later step.
Grant the operator admin on each new account, using the Identity Center username collected in Step 2:
NO_COLOR=1 npx -y @codyswann/aws-soc2-setup@1 sso assign \
-p <mgmt> -u <username> -a <account-id> -r AWSAdministratorAccess
(or sso group -g <prefix>-admins --all-users -a <account-id> -r AWSAdministratorAccess
for group-based access. If the user doesn't exist yet, create it first with
sso create-user.)
The CLI does not generate AWS CLI profiles — write them yourself. Append to
~/.aws/config (never overwrite existing sections; skip blocks that already
exist):
[sso-session <prefix>]
sso_start_url = <start-url>
sso_region = <region>
sso_registration_scopes = sso:account:access
[profile <prefix>-dev]
sso_session = <prefix>
sso_account_id = <dev-account-id>
sso_role_name = AWSAdministratorAccess
region = <region>
<start-url> is the full Identity Center start URL collected in Step 2 —
paste it verbatim, never rebuild it from a domain fragment. One
[profile <prefix>-<env>] block per environment — this exact naming is
what scripts/pre-deployment-checklist.sh and the db-connect scripts resolve.
Then authenticate and verify every profile maps to the expected account:
aws sso login --sso-session <prefix>
for env in dev staging production shared; do
aws sts get-caller-identity --profile <prefix>-$env --query Account --output text
done
Bootstrap the shared (pipeline) account first, then each stage account with
trust back to shared. Honor the project's CDK_BOOTSTRAP_QUALIFIER and
CDK_BOOTSTRAP_EXECUTION_POLICY_ARN env vars rather than hardcoding their
defaults (config/env.ts defaults: hnb659fds /
arn:aws:iam::aws:policy/AdministratorAccess):
qualifier="${CDK_BOOTSTRAP_QUALIFIER:-hnb659fds}"
exec_policy="${CDK_BOOTSTRAP_EXECUTION_POLICY_ARN:-arn:aws:iam::aws:policy/AdministratorAccess}"
npx cdk bootstrap aws://<shared-id>/<region> --profile <prefix>-shared \
--qualifier "$qualifier"
for env in dev staging production; do
npx cdk bootstrap aws://<env-id>/<region> --profile <prefix>-$env \
--qualifier "$qualifier" \
--trust <shared-id> \
--cloudformation-execution-policies "$exec_policy"
done
The --trust flag is the manual chicken-and-egg step the project's
trust-policy-stack documents as CfnOutputs — this skill is where it actually
runs.
config/environments.ts (when the project has it): replace each
environment's accountId: "PLACEHOLDER" with the real 12-digit ID from the
Step 4 map, and confirm region matches. This single edit is what flips
each environment from synth-only to deployable. If the project uses a
different layout, skip with a note — the .lisa.config.json record below
still applies..github/workflows/ci.yml references them:
gh secret set AWS_ACCOUNT_ID_DEV --body "<dev-id>"
gh secret set AWS_ACCOUNT_ID_STAGING --body "<staging-id>"
gh secret set AWS_ACCOUNT_ID_PRODUCTION --body "<production-id>"
gh secret set AWS_INFRA_ACCOUNT_ID --body "<shared-id>"
.lisa.config.json — record the machine-readable account map (merge
with jq, preserving every other key):
{
"aws": {
"region": "<region>",
"profilePrefix": "<prefix>",
"accounts": {
"dev": "<dev-id>",
"staging": "<staging-id>",
"production": "<production-id>",
"shared": "<shared-id>"
}
}
}
<prefix>-<env> profile authenticates to its expected account ID
(aws sts get-caller-identity).scripts/pre-deployment-checklist.sh passes when the project ships it.npx cdk synth succeeds and no environment is skipped as non-deployable.config/github.ts
codeConnectionArn) is an interactive console step outside this skill's
scope, and hardening extras (root remove-access, scp deny-iam-users,
scp alert-management) are offered but require explicit confirmation since
they are org-wide and destructive.The same SKILL.md ships to every supported runtime via the plugin build; all logic lives here, never in the command shim.
/lisa-cdk:setup:aws command, or invoke the
skill directly.$setup-aws-accounts (the command shim also
installs as $lisa-setup-aws).--dry-run (or status) first and show the plan before mutating an
organization. root and scp subcommands are destructive org-wide — never
run them without explicit user confirmation in this session.list-accounts before provision-account; read
~/.aws/config before appending profile blocks; never clobber existing
profiles or config keys.aws organizations list-accounts, not from scraped
CLI text.config/environments.ts and .lisa.config.json.development
Prepare a machine — a fresh laptop or a throwaway container — to run coding agents, before any repository exists. Detects which of Lisa's supported agents (Claude Code, Codex, Cursor, OpenCode, Antigravity, Copilot) are already installed, asks which credential manager the machine uses (Bitwarden, 1Password, Doppler, Vault, AWS, or none), and installs only what is missing, each by its vendor's own preferred method. Idempotent, headless by default, and emits a Dockerfile for a spin-up/spin-down environment. Run it on a new machine, in a container, or before cloning anything.
tools
Provision and verify a remote execution environment for a host project — Codex Cloud today, other remote surfaces as they are added. Generates a repository-owned setup script that installs the declared toolchain, materializes secrets through lisa-secrets-access, and runs the project's own hook. Provisions by API where one exists, by driving the vendor console where one does not, and by emitting exact config otherwise — then proves the result with the same read-back regardless of which tier did the work. Use before dispatching any work with executionEnv.
tools
Bring a developer's machine in line with the toolchain the project declares. Reports every tool in remoteEnv.tools that is missing, outdated, or unpinned for this platform, and installs the missing ones into ~/.local/bin from the same pinned, checksummed entries the remote surfaces use — but only when asked. Same manifest, same pins, same installers as lisa-setup-remote-env; what differs is consent and that the pin is a floor rather than an equality. Run it on a fresh checkout, after a manifest change, or when a tool fails at the moment of use.
tools
Route one unit of work to a remote execution surface. Reads the executionEnv parameter (local by default, codex-cloud or claude-web today), verifies the environment is provisioned and bound to this repository, submits a thin skill invocation, records the task identifier to .lisa/remote-dispatch.json, and exits without polling. Routing only — the remote runs the identical skill from the identical repository. Composable and inline: other skills invoke it via the Skill tool rather than users calling it directly.