Skip to main content
Running a single AWS account:
  1. Install the Fjall CLI.
  2. Connect your AWS account with fjall connect (see Getting Started).
  3. Scaffold the account infrastructure with fjall create account.
Running an AWS Organisation? Complete these first:
Deploy account infrastructure to your connected AWS account. In an organisation, deploy it to every member account other than the root or platform account. The account baseline holds the deploy role, the monitoring and audit roles, and the shared account-wide resources that applications build on. It runs once per account and region, before any application deploy.

Scaffold the account infrastructure

fjall account deploy deploys the CDK app at fjall/account/infrastructure.ts. fjall create account writes that file:
Run this once per project. If you scaffolded with fjall create organisation, the account component is already in place, so skip straight to the deploy.

Select the deployment target

A deployment target is an account and region pair, derived from your organisation config. Target names follow <account-name>-<abbreviated-region>, for example production-use1 or development-euw1. There is no manual AWS profile step. List the available targets:
The active target is marked with a ▸ and repeated on a trailing Active: line. Set a different one by name:
Confirm the active target:
fjall target set writes activeTarget to .fjall/local.json, which is not committed. It requires a Fjall login, and inside a project it requires the project’s organisation binding to match your current credential. Outside a project it refuses unless you pass --create-config. If the target’s region is not yet one of the organisation’s configured regions, it refuses unless you pass -y, --yes, which adds that region to the organisation config.
fjall account deploy auto-selects when the organisation has exactly one member account, so a single account spanning two regions still resolves without a prompt and you can skip this step. To override the active target for a single deploy, pass --target <name>. An unknown target name fails with the list of valid names, never a silent fallback.

Deploy the account

From the root of your project, run:
To deploy a specific member account without changing the active target, override it inline:
Tier deploys are noun-verb. fjall deploy account is retired and fails before authentication with a teaching error. fjall deploy takes application names only.

What happens

The deploy runs five steps:
  1. Authenticating with AWS: mints short-lived credentials for the resolved target. With both AWS_ACCESS_KEY_ID and AWS_SECRET_ACCESS_KEY set, it mints nothing and uses those keys instead (see Environment credentials override derivation): the account stack then deploys into whichever account the keys belong to, whatever --target or the active target names, in AWS_REGION, else AWS_DEFAULT_REGION, else us-east-2. Only the organisation’s management and platform accounts are refused. Unset AWS_ACCESS_KEY_ID, AWS_SECRET_ACCESS_KEY and AWS_SESSION_TOKEN to deploy where the target says.
  2. Connect securely: establishes the AWS context for the account and region.
  3. Prepare environment: synthesises the CloudFormation template and compares template hashes.
  4. Deploy infrastructure: applies the account stack.
  5. Enable monitoring: wires up the account monitoring resources.
The account stack provisions: The IAM globals (deploy role, monitoring role, audit role, CloudTrail, S3 Block Public Access) carry fixed names, so they are created only in the account’s home region. Other regions get the region-local resources. VPCs and security groups are not part of this stack. Fjall creates them per application at application deploy time. Security services (GuardDuty, Security Hub, AWS Config recorder, IAM Access Analyzer, Amazon Inspector) render into the account scaffold only when it was generated by fjall create organisation --security <tier>. A standalone fjall create account scaffold carries no governance profile.

The deploy role is capped by a permissions boundary

The account stack creates FjallDeployBoundary<orgId>, an account-global customer-managed policy, and attaches it to the FjallDeploy<orgId> role as its permissions boundary. This happens by default, whether or not the scaffold carries a governance profile: a scaffold with no securityTier still gets the hardened boundary, because the tier chooses which ceiling the role has, never whether it has one. The full policy, the three tiers and what each denies are documented on fjall connect, which installs the same policy through the quick-create template. To change the tier, or to opt out, edit the Account props in fjall/account/infrastructure.ts:
fjall create organisation --security <tier> writes securityTier into this file for you. There is no CLI flag for either setting on a deploy: both are properties of the infrastructure you are deploying, so they live in the file.
The opt-out is a create-time decision. Setting deployRoleBoundary: false on a role that is already capped fails the deploy, because the boundary denies iam:DeleteRolePermissionsBoundary — the role cannot remove its own ceiling. Uncapping an existing role is an administrator step from a principal outside the boundary. Setting securityTier and deployRoleBoundary: false together contradicts itself and fails at synth.

The boundary lands in two passes

The policy has to exist in the account before a bootstrap can point CDK’s execution role at it, so a new account converges over two deploys: Bootstrapping is per account and region, so each region converges on its own schedule. A region you deploy into later is capped by the first deploy that reaches it — usually an application deploy — not retroactively by the account deploy. Nothing is ever silently left uncapped. The first pass logs the state and the cure:
and the second logs the cap being applied:
A deploy that cannot read the policy — a throttled or denied iam:GetPolicy — warns and bootstraps without passing the boundary flag. That never loosens anything: CloudFormation carries the toolkit stack’s previous value through unchanged, so an already-capped role stays capped. A CDK line reading Removing existing permissions boundary during such a run is misleading and can be ignored; the parameter is carried over, not cleared.

Check which roles are capped

ListRoles does not return the PermissionsBoundary field, so any check built on a role listing reports every role as uncapped. Read it per role with get-role instead, through fjall aws exec so the call runs on Fjall-minted credentials:
A capped role answers with the boundary object — PermissionsBoundaryType of Policy and a PermissionsBoundaryArn ending in FjallDeployBoundary<orgId>. An uncapped one answers null. Run it once per deployment target to see the whole estate. fjall target list gives you the target names, and fjall org get gives you the organisation id. To read the policy itself, including which version is live and how many roles it caps:

The disaster-recovery backup vault is re-owned, not recreated

From fjall 38, a deploy into a production or compliance account with a DR region set probes who owns the fixed-name vault backupVault before it synthesises. The probe reads backup:DescribeBackupVault, falling back to backup:ListBackupVaults when that call is denied, because AWS Backup denies the describe for a vault that does not exist. It then reads a paginated cloudformation:ListStackResources on the account stack, and kms:DescribeKey on the live key when the vault is not a stack resource. The verdict picks one of three templates: A vault’s EncryptionKeyArn cannot be changed, so a re-owned vault always names the key it already has. A vault outside the stack is imported, not referenced. The run deploys once declaring identity only, with CloudFormation’s --import-existing-resources, re-probes, then deploys a second time to converge the vault’s tags and access policy. Both passes run inside the Deploy infrastructure step of the five above, so no extra step appears. The first pass logs the import:
An account whose vault needs importing deploys even when the template is unchanged. An import pass is never skipped as a no-op, so -f, --force is not needed.
A vault that carried a hand-written access policy loses it on re-own. The converge deploy applies the stack’s tags, and on a compliance account it sets the vault’s access policy to the DR cross-account copy grant. Nothing else survives, so re-apply the rest as configuration, not by hand.
A re-owned vault does not apply the account’s configured vaultLock and declares no lock of its own. Fjall never issues a lock call against a vault it did not create. When the live lock and the configured intent differ, synth emits the fjall:backupVault:lockIntentNotApplied warning:
Because the template declares no lock in this mode, the synth gate on acknowledgeImmutableVaultLock does not apply to a re-owned vault. It guards only a lock the template itself creates. fjall org deploy still refuses, before synth, any account configured vaultLock: "compliance" without the acknowledgement, whatever the vault verdict.

Backup vault refusals

These refusals stop the deploy at the vault probe or at the converge: The first three are probe refusals. On a direct fjall account deploy they fail the Prepare environment step, before anything in AWS changes, and the message carries no prefix. In an organisation cascade, fjall org deploy prefixes the same message with Backup vault probe failed for <account> and fails that account alone, inside its Deploying accounts step. The last two are converge refusals. They fail after the import pass has landed, in the Deploy infrastructure step of a direct fjall account deploy and in the Deploying accounts step of a cascade. Any read the probe cannot complete also refuses. A failed DescribeBackupVault, a failed vault listing, a failed stack listing or a failed DescribeKey names the call that failed and stops the deploy rather than guess who owns the vault. One more refusal reaches the same deploy from outside the probe. An assembly synthesised by @fjall/components-infrastructure older than 38.0.0 cannot read the ownership verdict, so a re-owned vault refuses in the Prepare environment step with was synthesised by constructs that predate the ownership verdict. Pin @fjall/components-infrastructure to >=38.0.0 and re-synth. FJALL_ALLOW_ENGINE_SKEW does not lift this one, and Engine Compatibility carries the full message and cure.

Output

Interactive runs finish with a step tick-list titled Deploying account: account, then a Next Steps section pointing at fjall deploy. When the template is unchanged you see No changes to deploy. Your infrastructure is up to date. instead. Non-interactive runs print a summary block:

Options

--region, --all-regions and --skip-build belong to the application command, fjall deploy <app>. The tier leaves register only their own flags, so passing one of those here fails with error: unknown option. Use --target <name> instead, which carries the region.
“Standalone” here means an account deployed before any provider account is registered with an organisation. Once the account is org-membered, its stage lives in the organisation registry and is set by fjall connect or the web app. Passing -e, --environment to an org-membered account fails with a teaching error rather than restaging it.

Next Steps

Create Application

Scaffold your first application’s infrastructure.

Deploy Application

Deploy an application to your managed AWS account.

Understanding Profiles

See how Fjall derives AWS credentials and deployment targets.

Deployment Safety

Review the approval gates that guard destructive changes.