Skip to main content
Interactive command. Opens your browser to deploy an OIDC CloudFormation stack, then waits for the stack to report back.

Usage

fjall connect establishes an OIDC trust between Fjall and one AWS account. Use it to:
  • Register the AWS Organizations management account, the first connect after fjall create organisation.
  • Connect a standalone AWS account after fjall create account.
  • Add another member account to an existing organisation.
fjall create organisation and fjall create account only scaffold project files. Neither establishes AWS trust, so fjall connect is a separate step in both journeys.

Prerequisites

  • Authenticated with fjall login.
  • Permission in the target AWS account to create IAM roles and CloudFormation stacks.
  • Signed in to the target AWS account in your browser.

What It Does

  1. Checks for existing connections and reads your organisation config.
  2. Opens your default browser on an AWS CloudFormation quick-create link.
  3. The stack creates an IAM OIDC provider and a role that Fjall assumes on demand.
  4. Polls until the stack completes, printing an elapsed-time line.
  5. Validates the OIDC trust and records the connection against your organisation.
The four progress steps are Checking existing connections, Opening AWS CloudFormation, Waiting for CloudFormation, and Validating OIDC connection.

What This Creates in Your Account

fjall connect deploys one CloudFormation stack, FjallOIDCConnector. Every resource it creates carries the tags fjall:managed=true and fjall:org-id=<your organisation id>. <orgId> is your Fjall organisation id, so the deploy role’s ARN is arn:aws:iam::<account id>:role/fjall/FjallDeploy<orgId>.

The deploy role holds AdministratorAccess

The deploy role’s attached managed policy is arn:aws:iam::aws:policy/AdministratorAccess. Fjall provisions VPCs, ECS services, RDS and Aurora clusters, S3 buckets, Lambda functions, CloudFront distributions, Route 53 zones, ACM certificates, IAM roles and CloudFormation stacks, and a hand-scoped policy would break every time Fjall adds a resource type. AdministratorAccess is attached to exactly one role in the stack. What that role can actually do is AdministratorAccess intersected with the permissions boundary, so read the two documents together.

The permissions boundary is on by default

The boundary is a customer-managed policy named FjallDeployBoundary<orgId>. The template’s FjallPermissionsBoundary parameter controls whether it is applied: it accepts true or false and defaults to true. fjall connect sets it to true explicitly on the quick-create link it opens, so a stale published template cannot drop the cap. A boundary is an intersection, not a grant, so the document leads with Allow on */* and then denies actions no deploy or destroy performs. The set of denies is a governance tier. Three tiers exist and they are cumulative: foundation is a subset of compliance, which is a subset of hardened. The quick-create template has no tier parameter, so fjall connect always installs hardened, and hardened is also what the CDK equivalent renders when no tier is set. The tier only ever changes which ceiling the role gets, never whether it has one. Every deny names an action CloudFormation never performs on your behalf, which is why they are narrower than they look: cloudtrail:StopLogging and not DeleteTrail, kms:DisableKey and not ScheduleKeyDeletion, config:StopConfigurationRecorder and not DeleteConfigurationRecorder. Destroying a trail, a key or a recorder as part of a stack teardown still works. The full document is in the reference below. To pick a tier other than hardened, see the governance profile on fjall create organisation.

The boundary policy is an account-global singleton

FjallDeployBoundary<orgId> is one policy per AWS account, shared by every Fjall stack in it, and it is deliberately not owned by any stack. A Custom::FjallDeployBoundary resource adopts the policy by name when it already exists and creates it otherwise, exactly as the OIDC provider resource does. That shape is what makes connecting a second account, or re-connecting an existing one, safe. A stack-owned managed policy could never be deleted once cdk bootstrap had capped an execution role with it (DeleteConflict), and a retained one would fail the next connect with EntityAlreadyExists. The writer only ever publishes a document matching one of the three tiers, and it will not weaken a policy that is already in place:
  • Deploying the same tier is a no-op — nothing is written.
  • Deploying a tighter tier converges the policy in place as a new default version. IAM caps a policy at five versions, so the oldest non-default version is rotated out first.
  • Deploying a looser tier fails the stack update rather than lowering the ceiling. Widening is an administrator action, run from a principal outside the boundary.
The ratchet compares the set of denied actions, not statement scope — a policy someone has tightened by hand at the resource or condition level converges back to its tier on the next deploy. If a tier upgrade is interrupted. CloudFormation gives a custom resource one attempt. If the writer publishes the tighter tier but its response is lost, the stack times out and rolls back, re-presenting the previous tier — and the ratchet refuses it, because the policy now carries the tighter document. The stack lands in UPDATE_ROLLBACK_FAILED, always on the stricter side. Recover with the same credentials that ran the deploy:
then re-run the connect or deploy that carried the tier change; it finds the policy already at the target tier and writes nothing. FjallDeployBoundaryPolicy is the resource’s logical id in the quick-create template; a CDK-rendered connector uses the hashed id of its DeployBoundary construct, which aws cloudformation list-stack-resources shows. Setting the policy’s default version back by hand is not an alternative for the deploy role: iam:SetDefaultPolicyVersion on the boundary is one of the actions the boundary denies to itself, so that path is administrator-only.

The CDK execution role is capped by the same policy

Fjall deploys through CDK, which assumes its own bootstrap execution role, cdk-hnb659fds-cfn-exec-role-<account id>-<region>. That role also holds AdministratorAccess, so capping the deploy role alone would leave the hand-off wider than the role that made it. Every fjall bootstrap therefore looks for FjallDeployBoundary<orgId> in the account and, when it is there, passes it to cdk bootstrap as the execution role’s boundary. That is the second entry in the boundary’s own self-protection scope, role/cdk-*-cfn-exec-role-*. The two roles are capped on different schedules, because the policy has to exist before a bootstrap can point at it. See two-pass convergence for what that means on a new account and how to check where you are.

Turning the boundary off

There are two opt-outs, one per lane, and both are create-time decisions: Either one renders no boundary policy and leaves FjallDeploy<orgId> holding AdministratorAccess with nothing above it. Because the policy is also what caps the CDK execution role, opting out uncaps both roles: nothing then stops a mistaken or compromised deploy leaving the organisation, closing the account, stopping CloudTrail, disabling a KMS key, or minting IAM users with standing credentials.
You cannot switch the boundary off after the fact. The boundary denies iam:DeleteRolePermissionsBoundary, so a role that is already capped cannot be uncapped by a deploy — setting deployRoleBoundary: false on a bounded role fails the stack update. Removing an existing cap is an administrator step: aws iam delete-role-permissions-boundary on each bounded role, run from a principal outside the boundary, then aws iam delete-policy.Turning it back on is self-service: re-run fjall connect with the parameter at its true default, or drop deployRoleBoundary: false from fjall/account/infrastructure.ts and redeploy.
deployRoleBoundary is not called permissionsBoundary on purpose. CDK’s own permissionsBoundary on StackProps is a different, stack-wide mechanism; this one governs the FjallDeploy role only. Setting both securityTier and deployRoleBoundary: false contradicts itself and fails at synth rather than silently picking one.

What the boundary does not stop

The boundary is a ceiling on accidental and negligent CloudFormation actions, and on the direct self-edits in the table above. It is not a hard cap against a hostile session that already holds the deploy role. That session still has AdministratorAccess minus the denies, so it could assume an unbounded role that already exists in the account — OrganizationAccountAccessRole exists in every member account — rather than escalating in place. Treat the boundary as one layer. The others are the OIDC trust policy above it (the deploy role is assumable only by your organisation’s subjects), the one-hour session cap, and, in an organisation, the service control policies the Enforced governance profile installs.

Sessions last one hour

MaxSessionDuration on the deploy role is 3600 seconds. Credentials are minted per operation and are not refreshed while a command runs, which is why fjall aws exec caps --timeout at 1h (default 10m). Split longer work into shorter phases.
Development accounts get four extra resources. When --environment development is passed, the stack also creates the managed policy FjallDevBoundary and the roles FjallDevDeploy<orgId>, FjallDevProvisioner<orgId> and FjallDevSyncWriter<orgId>. The three roles carry narrow inline policies scoped to dev-tier resources, never AdministratorAccess. No other stage renders them.
The OIDC provider outlives the stack. An IAM OIDC provider is account-global, one per account and issuer URL, shared with the Fjall account-management stack and trusted by other Fjall roles. The custom resource acknowledges a CloudFormation delete without removing it, and the boundary policy is handled the same way.Deleting the FjallOIDCConnector stack therefore removes the deploy role but leaves the OIDC provider and FjallDeployBoundary<orgId> in place. A later connect adopts both instead of failing with EntityAlreadyExists. To restore an account whose connector stack was torn down, use fjall reconnect, which repairs trust in place without re-tiering or renaming the account.

Removing the boundary policy at end of life

Because no stack owns the policy, no stack deletion removes it. fjall org destroy does, as a reference-counted sweep: for each account, once that account’s stack is gone, it checks whether any role, user or group still lists FjallDeployBoundary<orgId> and deletes the policy only when nothing does. It never un-caps a role to make the delete succeed. The order matters, and it is why a first destroy pass usually leaves the policy behind:
  1. Destroy the account’s stacks first. The FjallDeploy role goes with them, and while it exists it counts as a live reference.
  2. The CDK execution role in every bootstrapped region carries the same policy, and it lives in the CDKToolkit stack, outside everything Fjall destroys. While any of those survive, the policy is retained by design.
  3. Remove the bootstrap stacks — or un-cap those roles from an administrator principal — and re-run the destroy. The sweep then removes the policy.
A retention is never silent. The destroy transcript names the principal holding the reference and the cure:
A left-behind policy is harmless. It grants nothing on its own, and the next connect into that account adopts it.

Reference: the IAM documents the stack installs

Reference copies of the two policy documents, so you can diff them against the stack in the CloudFormation console before you create it. Both are shown with the template’s parameters named. Trust policy on FjallDeploy<orgId>:
Three details a reviewer should read carefully:
  • FjallMachineSubject matches on StringEquals, so org:${FjallOrgId}:* is an exact literal subject, not a wildcard. Only that sentinel and the account-scoped org:${FjallOrgId}:acct:${AWS::AccountId} match.
  • FjallUserSubjects matches on StringLike, so org:${FjallOrgId}:user:* does match any user subject in your organisation.
  • The template branches the trust policy on the account’s tier. Both branches currently render the document above.
Permissions boundary FjallDeployBoundary<orgId>:

Interactive Flow

Run fjall connect with no flags in a terminal. It prompts only for what it cannot already determine: After the prompts, the browser opens automatically. If it does not, the CLI prints If the browser doesn't open automatically, visit: <url>. On success the interactive frame shows the completed step list, the next command to run (fjall account deploy), and a docs link. The Account ID, Status, Region and External ID block belongs to the plain-CLI output (--non-interactive, --no-wait, or agent mode).
First connect after fjall create organisation. When the fjall/organisation scaffold exists locally and no AWS account has connected yet, Fjall skips the region and environment prompts and registers the account as the AWS Organizations management account (tier organisation). Only the name prompt is shown. A standalone fjall/account scaffold does not qualify, so a solo account is never silently rooted.
If the account is already connected and active, fjall connect short-circuits with AWS account already connected and exits without touching AWS.

Options

--environment platform is structural, not a workload stage. It registers the account on the platform tier, which hosts shared infrastructure such as IPAM and Transit Gateway. Platform accounts are filtered out of fjall accounts list, no application can target them, and deploying to one is a governance operation. Choose platform only for a genuine shared-services account.
The management account’s tier is set by context during organisation setup, never by --environment.

Agent options

For AI-agent and scripted use, connect and connect status also accept the standard agent flags:

Examples

Interactive

Connect a named staging account

Non-interactive

--environment is required here. Without it the command fails with the valid stage list, unless this is the first connect after fjall create organisation.

Scripted, without blocking on CloudFormation

--no-wait returns as soon as the quick-create URL is issued, so a script can hand the URL to an operator and poll separately.

Re-issue a stack for an existing connection

Typical workflow

Standalone account:
Organisation:

Check Connection Status

fjall connect status reads the connection lifecycle, including accounts marked disconnected.
The overview prints one row per connection as <account id> <status> tier=<tier> stage=<stage> <name>. The single-connection form prints Status, Phase, and, where the server supplies them, Reason, Cure and Account.

Restore a Disconnected Account

fjall reconnect restores OIDC trust for an account the scanner has marked disconnected, for example after the connector stack was torn down. It repairs trust in place, without re-tiering or renaming the account.
Use fjall reconnect for a disconnected account and --force for a connection that is already active. The two cases are different and the CLI routes them separately.

Troubleshooting

Browser does not open

Copy the URL from the terminal output and open it manually, then sign in to the correct AWS account.

Stack deployment fails

Check that the AWS account can create IAM roles and CloudFormation stacks, then rerun fjall connect to re-open the browser flow.

Non-interactive run rejects --environment

--environment accepts workload stages only. Pass one of production, staging, development, platform or compliance. root is not a valid input, because the management account’s tier comes from context.

Connection already exists

fjall connect short-circuits on a matching active connection. To connect a different AWS account, sign in to that account in your browser and rerun. To re-issue the stack for the same account, region and environment, add --force.

Account shows as disconnected

Run fjall connect status to confirm the phase, then fjall reconnect to restore trust. --force is the wrong cure here.
The OIDC connection issues short-lived credentials, so no long-term secrets are stored. Fjall assumes the IAM role on demand through OpenID Connect federation, and never reads or writes ~/.aws/config.

Next Steps

Deploy an account

Run fjall account deploy to provision the account tier after connecting.

Deploy an organisation

Create member accounts and provision every tier with fjall org deploy.

fjall create

Scaffold an organisation, an account, or an application.

Understanding Profiles

See how AWS profiles and deployment targets are derived from org config.