@fjall/deploy-core 2.4.5 → 2.4.8

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/.minified CHANGED
@@ -1 +1 @@
1
- 116 files minified at 2026-05-27T20:39:12.628Z
1
+ 116 files minified at 2026-05-27T22:11:30.371Z
@@ -1 +1 @@
1
- import{CreateOrganizationalUnitCommand as p,ListOrganizationalUnitsForParentCommand as h,ListParentsCommand as S,MoveAccountCommand as A}from"@aws-sdk/client-organizations";import{success as u,failure as d}from"@fjall/generator";import{extractErrorName as U,isOULeaf as y,SDK_TIMEOUT_MS as w,AWS_ERROR_NAMES as I}from"./types.js";import{getErrorMessage as O}from"@fjall/util";async function N(o,a){const t=await o.send(new h({ParentId:a}),{abortSignal:AbortSignal.timeout(w)});let e=t.OrganizationalUnits??[],n=t.NextToken;for(;n;){const r=await o.send(new h({ParentId:a,NextToken:n}),{abortSignal:AbortSignal.timeout(w)});e=e.concat(r.OrganizationalUnits??[]),n=r.NextToken}return e}async function L(o,a){try{return(await o.send(new S({ChildId:a}),{abortSignal:AbortSignal.timeout(w)})).Parents?.[0]?.Id}catch(t){if(U(t)===I.CHILD_NOT_FOUND)return;throw t}}async function C(o,a,t,e){const n=e.find(r=>r.Name===t);if(n?.Id)return u(n.Id);try{const s=(await o.send(new p({Name:t,ParentId:a}),{abortSignal:AbortSignal.timeout(w)})).OrganizationalUnit;return s?.Id?u(s.Id):d(new Error(`OU "${t}" was created but has no ID`))}catch(r){return d(new Error(`Failed to create OU "${t}": ${O(r)}`))}}async function P(o,a,t){const e={};if(t.length===0)return u(e);const n=await N(o,a);for(const r of t){const s=r.charAt(0).toUpperCase()+r.slice(1),i=await C(o,a,s,n);if(!i.success)return d(i.error);e[r.toLowerCase()]=i.data,n.push({Id:i.data,Name:s})}return u(e)}async function b(o,a,t,e,n,r){const s=await N(o,a);for(const[i,c]of Object.entries(t)){const f=await C(o,a,i,s);if(!f.success)return d(f.error);const l=f.data,g=n?`${n}.${i.toLowerCase()}`:i.toLowerCase();if(e[g]=l,n){const m=i.toLowerCase();r.has(m)||(e[m]=l)}if(s.push({Id:l,Name:i}),!y(c)){const m=await b(o,l,c,e,g,r);if(!m.success)return m}}return u(void 0)}async function _(o,a,t){try{if(Array.isArray(t))return await P(o,a,t);const e={},n=new Set(Object.keys(t).map(s=>s.toLowerCase())),r=await b(o,a,t,e,"",n);return r.success?u(e):d(r.error)}catch(e){return d(new Error(`Failed to ensure OUs exist: ${O(e)}`))}}function x(o,a,t=""){const e={};for(const[n,r]of Object.entries(o)){const s=t?`${t}.${n.toLowerCase()}`:n.toLowerCase(),i=a[s];if(y(r)){if(i)for(const c of r)e[c.toLowerCase()]=i}else Object.assign(e,x(r,a,s))}return e}async function D(o,a,t,e){try{if(t.length===0)return u({moved:0,alreadyPlaced:0});let n=0,r=0;for(const s of t){if(s.environment==="root")continue;const i=e?e[s.name.toLowerCase()]:a[s.environment.toLowerCase()];if(!i)continue;const c=await L(o,s.id);if(c){if(c===i){r++;continue}try{await o.send(new A({AccountId:s.id,SourceParentId:c,DestinationParentId:i}),{abortSignal:AbortSignal.timeout(w)}),n++}catch(f){if(U(f)===I.ACCOUNT_NOT_FOUND)continue;throw f}}}return u({moved:n,alreadyPlaced:r})}catch(n){return d(new Error(`Failed to place accounts in OUs: ${O(n)}`))}}export{x as buildAccountToOUMap,_ as ensureOrganisationalUnitsExist,D as placeAccountsInOUs};
1
+ import{CreateOrganizationalUnitCommand as A,ListOrganizationalUnitsForParentCommand as U,ListParentsCommand as L,MoveAccountCommand as P}from"@aws-sdk/client-organizations";import{success as u,failure as d}from"@fjall/generator";import{extractErrorName as y,isOULeaf as I,SDK_TIMEOUT_MS as m,AWS_ERROR_NAMES as N}from"./types.js";import{getErrorMessage as g}from"@fjall/util";async function C(r,a){const t=await r.send(new U({ParentId:a}),{abortSignal:AbortSignal.timeout(m)});let e=t.OrganizationalUnits??[],n=t.NextToken;for(;n;){const o=await r.send(new U({ParentId:a,NextToken:n}),{abortSignal:AbortSignal.timeout(m)});e=e.concat(o.OrganizationalUnits??[]),n=o.NextToken}return e}async function x(r,a){try{return(await r.send(new L({ChildId:a}),{abortSignal:AbortSignal.timeout(m)})).Parents?.[0]?.Id}catch(t){if(y(t)===N.CHILD_NOT_FOUND)return;throw t}}async function b(r,a,t,e){const n=e.find(o=>o.Name===t);if(n?.Id)return u(n.Id);try{const s=(await r.send(new A({Name:t,ParentId:a}),{abortSignal:AbortSignal.timeout(m)})).OrganizationalUnit;return s?.Id?u(s.Id):d(new Error(`OU "${t}" was created but has no ID`))}catch(o){return d(new Error(`Failed to create OU "${t}": ${g(o)}`))}}function S(r){return r.charAt(0).toUpperCase()+r.slice(1)}async function E(r,a,t){const e={};if(t.length===0)return u(e);const n=await C(r,a);for(const o of t){const s=S(o),i=await b(r,a,s,n);if(!i.success)return d(i.error);e[o.toLowerCase()]=i.data,n.push({Id:i.data,Name:s})}return u(e)}async function p(r,a,t,e,n,o){const s=await C(r,a);for(const[i,c]of Object.entries(t)){const f=S(i),w=await b(r,a,f,s);if(!w.success)return d(w.error);const O=w.data,h=n?`${n}.${i.toLowerCase()}`:i.toLowerCase();if(e[h]=O,n){const l=i.toLowerCase();o.has(l)||(e[l]=O)}if(s.push({Id:O,Name:f}),!I(c)){const l=await p(r,O,c,e,h,o);if(!l.success)return l}}return u(void 0)}async function k(r,a,t){try{if(Array.isArray(t))return await E(r,a,t);const e={},n=new Set(Object.keys(t).map(s=>s.toLowerCase())),o=await p(r,a,t,e,"",n);return o.success?u(e):d(o.error)}catch(e){return d(new Error(`Failed to ensure OUs exist: ${g(e)}`))}}function T(r,a,t=""){const e={};for(const[n,o]of Object.entries(r)){const s=t?`${t}.${n.toLowerCase()}`:n.toLowerCase(),i=a[s];if(I(o)){if(i)for(const c of o)e[c.toLowerCase()]=i}else Object.assign(e,T(o,a,s))}return e}async function v(r,a,t,e){try{if(t.length===0)return u({moved:0,alreadyPlaced:0});let n=0,o=0;for(const s of t){if(s.environment==="root")continue;const i=e?e[s.name.toLowerCase()]:a[s.environment.toLowerCase()];if(!i)continue;const c=await x(r,s.id);if(c){if(c===i){o++;continue}try{await r.send(new P({AccountId:s.id,SourceParentId:c,DestinationParentId:i}),{abortSignal:AbortSignal.timeout(m)}),n++}catch(f){if(y(f)===N.ACCOUNT_NOT_FOUND)continue;throw f}}}return u({moved:n,alreadyPlaced:o})}catch(n){return d(new Error(`Failed to place accounts in OUs: ${g(n)}`))}}export{T as buildAccountToOUMap,k as ensureOrganisationalUnitsExist,v as placeAccountsInOUs};
@@ -14,3 +14,8 @@ import type { DeployServices } from "./serviceFactory.js";
14
14
  * responsibility. deploy-core receives credentials and deploys.
15
15
  */
16
16
  export declare function deployOrganisation(params: DeployParams, services: DeployServices, operation: OrganisationOperation): Promise<Result<DeployResult>>;
17
+ export interface OrgDetailsForSynth {
18
+ orgId: string;
19
+ rootId: string;
20
+ managementAccountId: string;
21
+ }
@@ -1,3 +1,3 @@
1
- import{success as K,failure as M}from"@fjall/generator";import{ORGANISATION_TYPES as h,getOrganisationStackName as U}from"../types/operations.js";import{CdkContextBuilder as Z}from"../services/supporting/CdkContextBuilder.js";import{stubCallerIdentity as tt}from"../types/deployment/index.js";import{buildParamsContext as et,collectStackOutputs as v,synthOrFail as B,bootstrapOrFail as V,forwardOutput as W,forwardResourceProgress as q}from"./contextHelpers.js";import{partitionAccounts as ot,deployCascadeAccount as z,readPlatformIpamPoolIds as nt,deployDomains as rt}from"./cascadeHelpers.js";import{maskSensitiveOutput as E}from"@fjall/util";import{INFRA_STEP_NAME as w,STEP_IDS as I}from"../types/stepDefinitions.js";async function ft(n,e,a){const s=Date.now();switch(a.type){case h.ORGANISATION:return at(n,e,a,s);case h.PLATFORM:return J(n,e,a,"platform",s);case h.ACCOUNT:return J(n,e,a,"account",s);default:{const t=a.type;return M(new Error(`Unsupported organisation type: ${String(t)}`))}}}function H(n,e,a,s,t){return Z.buildDeploymentContext({deployType:s,target:a.target,path:a.path,region:e.awsProvider.getRegion(),accountName:t,callerIdentity:tt(e.awsProvider.getAccountId()),...et({orgConfig:n.orgConfig,identity:n.identity,skipOidc:n.options?.skipOidc})},{verbose:n.options?.verbose,infraOnly:n.options?.infraOnly},n.orgConfig)}const o={CONNECT:{id:I.CONNECT,name:w.CONNECT},PREPARE:{id:I.PREPARE_ENVIRONMENT,name:w.PREPARE},DEPLOY:{id:I.DEPLOY,name:w.DEPLOY},MONITORING:{id:I.MONITORING,name:w.MONITORING},ORG_DEPLOY:{id:I.ORG_DEPLOY,name:"Deploying organisation infrastructure"}},d=4;async function J(n,e,a,s,t){const{callbacks:r}=n;r.onStepComplete?.(o.CONNECT.id,o.CONNECT.name,"completed",0,d),r.onStepStart?.(o.PREPARE.id,o.PREPARE.name,1,d);const m=H(n,e,a,s,s==="account"?a.target:void 0);r.onLog?.(`Synthesising ${s} infrastructure\u2026`,"info");const g=await B(e,m,r,"CDK synthesis failed");if(!g.success)return r.onStepComplete?.(o.PREPARE.id,o.PREPARE.name,"error",1,d),g;const R=await V(e,m,r);if(!R.success)return r.onStepComplete?.(o.PREPARE.id,o.PREPARE.name,"error",1,d),R;r.onStepComplete?.(o.PREPARE.id,o.PREPARE.name,"completed",1,d);const D=U(a.type);r.onStepStart?.(o.DEPLOY.id,o.DEPLOY.name,2,d);const u=await e.cdkService.runCdkDeploy(m,D,W(r),q(r),e.awsProvider);if(!u.success){r.onStepComplete?.(o.DEPLOY.id,o.DEPLOY.name,"error",2,d);const C=new Error(E(u.error));return r.onError?.(C),M(C)}r.onStepComplete?.(o.DEPLOY.id,o.DEPLOY.name,"completed",2,d);const f=await e.cfnService.getStackOutputs(D);f.success||r.onLog?.("Failed to read stack outputs (non-critical)","debug");const O=v(f);return r.onStepStart?.(o.MONITORING.id,o.MONITORING.name,3,d),r.onStepComplete?.(o.MONITORING.id,o.MONITORING.name,"completed",3,d),K({target:a.target,deploymentType:"organisation",outputs:O,durationMs:Date.now()-t})}async function at(n,e,a,s){const{callbacks:t,options:r}=n,m=n.orgConfig?.providerAccounts??[],g=H(n,e,a,"organisation"),R=r?.cascade!==!1,u=2+(R?m.length:0),{id:f,name:O}=o.PREPARE;t.onStepStart?.(f,O,0,u),t.onLog?.("Synthesising organisation infrastructure\u2026","info");const C=await B(e,g,t,"CDK synthesis failed");if(!C.success)return t.onStepComplete?.(f,O,"error",0,u),C;const Y=await V(e,g,t);if(!Y.success)return t.onStepComplete?.(f,O,"error",0,u),Y;t.onStepComplete?.(f,O,"completed",0,u);const{id:T,name:L}=o.ORG_DEPLOY;t.onStepStart?.(T,L,1,u);const _=U(h.ORGANISATION),$=await e.cdkService.runCdkDeploy(g,_,W(t),q(t),e.awsProvider);if(!$.success){t.onStepComplete?.(T,L,"error",1,u);const l=new Error(E($.error));return t.onError?.(l),M(l)}const x=await e.cfnService.getStackOutputs(_);x.success||t.onLog?.("Failed to read org stack outputs (non-critical)","debug");const Q=v(x);t.onStepComplete?.(T,L,"completed",1,u);const c=[],y=[];if(R&&m.length>0){t.onCascadeStart?.();let l=0,k=!1,F=!1;const{platformAccount:P,memberAccounts:b}=ot(m);if(P){t.onCascadePhaseStart?.("platform");const i=await z(n,e,a,P,"platform",t);i.success?(k=!0,i.data.outputs&&y.push({accountId:P.id,outputs:i.data.outputs})):c.push({accountId:P.id,error:i.error.message}),t.onCascadePhaseComplete?.("platform")}let j=new Map;if(k&&P&&(j=await nt(e,P,t)),n.domainProvider){const i=await rt(n.domainProvider,t);F=i.domainsDeployed>0;for(const N of i.errors)c.push({accountId:"domains",error:E(N)})}if(b.length>0){t.onCascadePhaseStart?.("accounts");const i=e.awsProvider.getRegion();(await Promise.allSettled(b.map(p=>{const G=i.replace(/-/g,""),S=j.get(`${p.id}-${G}`);return z(n,e,a,p,"account",t,S)}))).forEach((p,G)=>{const S=b[G];if(!S)return;if(p.status==="rejected"){c.push({accountId:S.id,error:E(p.reason instanceof Error?p.reason.message:String(p.reason))});return}const A=p.value;A.success?(l++,A.data.outputs&&y.push({accountId:S.id,outputs:A.data.outputs})):c.push({accountId:S.id,error:A.error.message})}),t.onCascadePhaseComplete?.("accounts")}if(t.onCascadeComplete?.({platformDeployed:k,domainsDeployed:F,accountsDeployed:l,accountsFailed:c.length,errors:c}),c.length>0){const i=c.map(N=>` ${N.accountId}: ${N.error}`).join(`
2
- `);t.onLog?.(E(`Cascade completed with ${c.length} failure(s):
3
- ${i}`),"warn")}}const X=c.length>0?c.map(l=>E(`${l.accountId}: ${l.error}`)):void 0;return K({target:a.target,deploymentType:"organisation",outputs:Q,...y.length>0?{cascadeOutputs:y}:{},durationMs:Date.now()-s,warnings:X})}export{ft as deployOrganisation};
1
+ import{success as _,failure as I}from"@fjall/generator";import{OrganizationsClient as ot}from"@aws-sdk/client-organizations";import{ORGANISATION_TYPES as D,getOrganisationStackName as v}from"../types/operations.js";import{CdkContextBuilder as et}from"../services/supporting/CdkContextBuilder.js";import{stubCallerIdentity as nt}from"../types/deployment/index.js";import{ensureOrganisationExists as rt}from"../aws/organisations/organisation.js";import{buildParamsContext as at,collectStackOutputs as z,synthOrFail as B,bootstrapOrFail as V,forwardOutput as W,forwardResourceProgress as q}from"./contextHelpers.js";import{partitionAccounts as st,deployCascadeAccount as H,readPlatformIpamPoolIds as ct,deployDomains as it}from"./cascadeHelpers.js";import{maskSensitiveOutput as g}from"@fjall/util";import{INFRA_STEP_NAME as T,STEP_IDS as w}from"../types/stepDefinitions.js";async function St(r,o,e){const s=Date.now();switch(e.type){case D.ORGANISATION:return ut(r,o,e,s);case D.PLATFORM:return X(r,o,e,"platform",s);case D.ACCOUNT:return X(r,o,e,"account",s);default:{const t=e.type;return I(new Error(`Unsupported organisation type: ${String(t)}`))}}}function J(r,o,e,s,t,a){return et.buildDeploymentContext({deployType:s,target:e.target,path:e.path,region:o.awsProvider.getRegion(),accountName:a,callerIdentity:nt(o.awsProvider.getAccountId()),orgId:t.orgId,rootId:t.rootId,managementAccountId:t.managementAccountId,...at({orgConfig:r.orgConfig,identity:r.identity,skipOidc:r.options?.skipOidc})},{verbose:r.options?.verbose,infraOnly:r.options?.infraOnly},r.orgConfig)}async function Q(r){const o=r.awsProvider.getClient(ot),e=await rt(o);return e.success?_({orgId:e.data.orgId,rootId:e.data.rootId,managementAccountId:e.data.managementAccountId}):I(e.error)}const n={CONNECT:{id:w.CONNECT,name:T.CONNECT},PREPARE:{id:w.PREPARE_ENVIRONMENT,name:T.PREPARE},DEPLOY:{id:w.DEPLOY,name:T.DEPLOY},MONITORING:{id:w.MONITORING,name:T.MONITORING},ORG_DEPLOY:{id:w.ORG_DEPLOY,name:"Deploying organisation infrastructure"}},d=4;async function X(r,o,e,s,t){const{callbacks:a}=r;a.onStepComplete?.(n.CONNECT.id,n.CONNECT.name,"completed",0,d),a.onStepStart?.(n.PREPARE.id,n.PREPARE.name,1,d);const f=await Q(o);if(!f.success){a.onStepComplete?.(n.PREPARE.id,n.PREPARE.name,"error",1,d);const p=new Error(g(f.error.message));return a.onError?.(p),I(p)}const E=J(r,o,e,s,f.data,s==="account"?e.target:void 0);a.onLog?.(`Synthesising ${s} infrastructure\u2026`,"info");const P=await B(o,E,a,"CDK synthesis failed");if(!P.success)return a.onStepComplete?.(n.PREPARE.id,n.PREPARE.name,"error",1,d),P;const N=await V(o,E,a);if(!N.success)return a.onStepComplete?.(n.PREPARE.id,n.PREPARE.name,"error",1,d),N;a.onStepComplete?.(n.PREPARE.id,n.PREPARE.name,"completed",1,d);const k=v(e.type);a.onStepStart?.(n.DEPLOY.id,n.DEPLOY.name,2,d);const l=await o.cdkService.runCdkDeploy(E,k,W(a),q(a),o.awsProvider);if(!l.success){a.onStepComplete?.(n.DEPLOY.id,n.DEPLOY.name,"error",2,d);const p=new Error(g(l.error));return a.onError?.(p),I(p)}a.onStepComplete?.(n.DEPLOY.id,n.DEPLOY.name,"completed",2,d);const O=await o.cfnService.getStackOutputs(k);O.success||a.onLog?.("Failed to read stack outputs (non-critical)","debug");const R=z(O);return a.onStepStart?.(n.MONITORING.id,n.MONITORING.name,3,d),a.onStepComplete?.(n.MONITORING.id,n.MONITORING.name,"completed",3,d),_({target:e.target,deploymentType:"organisation",outputs:R,durationMs:Date.now()-t})}async function ut(r,o,e,s){const{callbacks:t,options:a}=r,f=r.orgConfig?.providerAccounts??[],E=await Q(o);if(!E.success){const i=new Error(g(E.error.message));return t.onError?.(i),I(i)}const P=J(r,o,e,"organisation",E.data),N=a?.cascade!==!1,l=2+(N?f.length:0),{id:O,name:R}=n.PREPARE;t.onStepStart?.(O,R,0,l),t.onLog?.("Synthesising organisation infrastructure\u2026","info");const p=await B(o,P,t,"CDK synthesis failed");if(!p.success)return t.onStepComplete?.(O,R,"error",0,l),p;const x=await V(o,P,t);if(!x.success)return t.onStepComplete?.(O,R,"error",0,l),x;t.onStepComplete?.(O,R,"completed",0,l);const{id:L,name:b}=n.ORG_DEPLOY;t.onStepStart?.(L,b,1,l);const $=v(D.ORGANISATION),F=await o.cdkService.runCdkDeploy(P,$,W(t),q(t),o.awsProvider);if(!F.success){t.onStepComplete?.(L,b,"error",1,l);const i=new Error(g(F.error));return t.onError?.(i),I(i)}const j=await o.cfnService.getStackOutputs($);j.success||t.onLog?.("Failed to read org stack outputs (non-critical)","debug");const Z=z(j);t.onStepComplete?.(L,b,"completed",1,l);const c=[],y=[];if(N&&f.length>0){t.onCascadeStart?.();let i=0,G=!1,K=!1;const{platformAccount:S,memberAccounts:M}=st(f);if(S){t.onCascadePhaseStart?.("platform");const u=await H(r,o,e,S,"platform",t);u.success?(G=!0,u.data.outputs&&y.push({accountId:S.id,outputs:u.data.outputs})):c.push({accountId:S.id,error:u.error.message}),t.onCascadePhaseComplete?.("platform")}let U=new Map;if(G&&S&&(U=await ct(o,S,t)),r.domainProvider){const u=await it(r.domainProvider,t);K=u.domainsDeployed>0;for(const A of u.errors)c.push({accountId:"domains",error:g(A)})}if(M.length>0){t.onCascadePhaseStart?.("accounts");const u=o.awsProvider.getRegion();(await Promise.allSettled(M.map(m=>{const Y=u.replace(/-/g,""),C=U.get(`${m.id}-${Y}`);return H(r,o,e,m,"account",t,C)}))).forEach((m,Y)=>{const C=M[Y];if(!C)return;if(m.status==="rejected"){c.push({accountId:C.id,error:g(m.reason instanceof Error?m.reason.message:String(m.reason))});return}const h=m.value;h.success?(i++,h.data.outputs&&y.push({accountId:C.id,outputs:h.data.outputs})):c.push({accountId:C.id,error:h.error.message})}),t.onCascadePhaseComplete?.("accounts")}if(t.onCascadeComplete?.({platformDeployed:G,domainsDeployed:K,accountsDeployed:i,accountsFailed:c.length,errors:c}),c.length>0){const u=c.map(A=>` ${A.accountId}: ${A.error}`).join(`
2
+ `);t.onLog?.(g(`Cascade completed with ${c.length} failure(s):
3
+ ${u}`),"warn")}}const tt=c.length>0?c.map(i=>g(`${i.accountId}: ${i.error}`)):void 0;return _({target:e.target,deploymentType:"organisation",outputs:Z,...y.length>0?{cascadeOutputs:y}:{},durationMs:Date.now()-s,warnings:tt})}export{St as deployOrganisation};
@@ -1,6 +1,6 @@
1
1
  import { type Result } from "@fjall/generator";
2
2
  import type { AwsProvider } from "../aws/AwsProvider.js";
3
- import type { OUTree } from "../aws/organisations/types.js";
3
+ import type { OUTree, AccountInfo } from "../aws/organisations/types.js";
4
4
  export type OrgSetupPhase = "create-organisation" | "enable-policies" | "enable-service-access" | "enable-ram-sharing" | "activate-trusted-access" | "enable-ipam" | "configure-backup" | "create-accounts" | "create-organisational-units" | "place-accounts" | "activate-cost-tags" | "check-identity-centre" | "register-security-delegates";
5
5
  export interface OrgSetupCallbacks {
6
6
  onPhaseStart?(phase: OrgSetupPhase): void;
@@ -15,7 +15,7 @@ export interface OrgSetupConfig {
15
15
  }>;
16
16
  platformAccountId?: string;
17
17
  organisationalUnits: string[] | OUTree;
18
- accountPlacements?: Record<string, string>;
18
+ accountPlacements?: AccountInfo[];
19
19
  costAllocationTags?: string[];
20
20
  skipIdentityCentre?: boolean;
21
21
  securityDelegateAccountId?: string;
@@ -1 +1 @@
1
- import{success as y,failure as h}from"@fjall/generator";import{OrganizationsClient as U}from"@aws-sdk/client-organizations";import{RAMClient as v}from"@aws-sdk/client-ram";import{CloudFormationClient as T}from"@aws-sdk/client-cloudformation";import{EC2Client as M}from"@aws-sdk/client-ec2";import{BackupClient as D}from"@aws-sdk/client-backup";import{CostExplorerClient as N}from"@aws-sdk/client-cost-explorer";import{SSOAdminClient as j}from"@aws-sdk/client-sso-admin";import{ensureOrganisationExists as B}from"../aws/organisations/organisation.js";import{enablePolicyTypes as F}from"../aws/organisations/policies.js";import{enableServiceAccess as L}from"../aws/organisations/serviceAccess.js";import{enableRamSharing as W}from"../aws/organisations/ram.js";import{activateTrustedAccess as z}from"../aws/organisations/trustedAccess.js";import{enableIpamDelegatedAdmin as G}from"../aws/organisations/ipam.js";import{updateBackupGlobalSettings as K}from"../aws/organisations/backup.js";import{listAccounts as q,createAccount as H}from"../aws/organisations/accounts.js";import{ensureOrganisationalUnitsExist as J,placeAccountsInOUs as Q,buildAccountToOUMap as V}from"../aws/organisations/organisationalUnits.js";import{activateCostAllocationTags as X}from"../aws/organisations/costAllocation.js";import{checkIdentityCentreStatus as Y}from"../aws/organisations/identityCentre.js";import{registerSecurityDelegates as Z}from"../aws/organisations/delegatedAdmin.js";async function le(n,o,e){const r=[],s=[],t=[],c=[];let C;const u=n.getClient(U),A=n.getClient(v),S=n.getClient(T),I=n.getClient(M),E=n.getClient(D),w=n.getClient(N),O=n.getClient(j);e?.onPhaseStart?.("create-organisation"),e?.onProgress?.("Ensuring AWS Organisation exists");const p=await B(u);if(!p.success)return e?.onError?.("create-organisation",p.error),e?.onPhaseComplete?.("create-organisation","error"),h(p.error);const{orgId:R,rootId:f}=p.data;if(e?.onPhaseComplete?.("create-organisation","completed"),r.push("create-organisation"),await a("enable-policies",()=>(e?.onProgress?.("Enabling organisation policy types"),F(u,f)),r,t,e),await a("enable-service-access",()=>(e?.onProgress?.("Enabling AWS service access"),L(u)),r,t,e),await a("enable-ram-sharing",()=>(e?.onProgress?.("Enabling RAM sharing"),W(A)),r,t,e),await a("activate-trusted-access",()=>(e?.onProgress?.("Activating CloudFormation trusted access"),z(S)),r,t,e),o.platformAccountId){const i=o.platformAccountId;await a("enable-ipam",()=>(e?.onProgress?.("Enabling IPAM delegated administrator"),G(I,i)),r,t,e)}await a("configure-backup",()=>(e?.onProgress?.("Updating backup global settings"),K(E)),r,t,e),e?.onPhaseStart?.("create-accounts"),e?.onProgress?.("Checking for missing accounts");const d=await _(u,o.accounts,c);d.success?(r.push("create-accounts"),e?.onPhaseComplete?.("create-accounts","completed")):(t.push({phase:"create-accounts",error:d.error.message}),e?.onError?.("create-accounts",d.error),e?.onPhaseComplete?.("create-accounts","error"));let m={};e?.onPhaseStart?.("create-organisational-units"),e?.onProgress?.("Ensuring organisational units exist");const g=await J(u,f,o.organisationalUnits);if(g.success?(m=g.data,r.push("create-organisational-units"),e?.onPhaseComplete?.("create-organisational-units","completed")):(t.push({phase:"create-organisational-units",error:g.error.message}),e?.onError?.("create-organisational-units",g.error),e?.onPhaseComplete?.("create-organisational-units","error")),Object.keys(m).length>0&&o.accountPlacements){const i=$(o.accountPlacements),x=Array.isArray(o.organisationalUnits)?void 0:V(o.organisationalUnits,m);await a("place-accounts",()=>(e?.onProgress?.("Placing accounts in organisational units"),Q(u,m,i,x)),r,t,e)}else s.push("place-accounts"),e?.onPhaseStart?.("place-accounts"),e?.onPhaseComplete?.("place-accounts","skipped");const P=o.costAllocationTags??[];if(P.length>0?await a("activate-cost-tags",()=>(e?.onProgress?.("Activating cost allocation tags"),X(w,P.map(i=>({TagKey:i})))),r,t,e):(s.push("activate-cost-tags"),e?.onPhaseStart?.("activate-cost-tags"),e?.onPhaseComplete?.("activate-cost-tags","skipped")),o.skipIdentityCentre)s.push("check-identity-centre"),e?.onPhaseStart?.("check-identity-centre"),e?.onPhaseComplete?.("check-identity-centre","skipped");else{e?.onPhaseStart?.("check-identity-centre"),e?.onProgress?.("Checking Identity Centre status");const i=await Y(O);i.success?(C=i.data.enabled?"enabled":"not-enabled",r.push("check-identity-centre"),e?.onPhaseComplete?.("check-identity-centre","completed")):(t.push({phase:"check-identity-centre",error:i.error.message}),e?.onError?.("check-identity-centre",i.error),e?.onPhaseComplete?.("check-identity-centre","error"))}const l=o.securityDelegateAccountId;return l?await a("register-security-delegates",()=>(e?.onProgress?.("Registering security service delegated administrators"),Z(u,l)),r,t,e):(s.push("register-security-delegates"),e?.onPhaseStart?.("register-security-delegates"),e?.onPhaseComplete?.("register-security-delegates","skipped")),y({organisationId:R,createdAccounts:c,identityCentreStatus:C,phasesCompleted:r,phasesSkipped:s,errors:t})}async function a(n,o,e,r,s){s?.onPhaseStart?.(n);const t=await o();t.success?(e.push(n),s?.onPhaseComplete?.(n,"completed")):(r.push({phase:n,error:t.error.message}),s?.onError?.(n,t.error),s?.onPhaseComplete?.(n,"error"))}async function _(n,o,e){const r=await q(n);if(!r.success)return h(r.error);const s=new Set(r.data.map(t=>t.Name?.toLowerCase()).filter(t=>t!==void 0));for(const t of o){if(s.has(t.name.toLowerCase()))continue;const c=await H(n,t.name,t.email);if(!c.success)return h(c.error);e.push({name:c.data.accountName,accountId:c.data.accountId})}return y(void 0)}function $(n){return Object.entries(n).map(([o,e])=>({id:o,name:o,environment:e}))}export{le as runOrganisationSetup};
1
+ import{success as y,failure as h}from"@fjall/generator";import{OrganizationsClient as v}from"@aws-sdk/client-organizations";import{RAMClient as x}from"@aws-sdk/client-ram";import{CloudFormationClient as T}from"@aws-sdk/client-cloudformation";import{EC2Client as M}from"@aws-sdk/client-ec2";import{BackupClient as D}from"@aws-sdk/client-backup";import{CostExplorerClient as N}from"@aws-sdk/client-cost-explorer";import{SSOAdminClient as B}from"@aws-sdk/client-sso-admin";import{ensureOrganisationExists as F}from"../aws/organisations/organisation.js";import{enablePolicyTypes as L}from"../aws/organisations/policies.js";import{enableServiceAccess as W}from"../aws/organisations/serviceAccess.js";import{enableRamSharing as j}from"../aws/organisations/ram.js";import{activateTrustedAccess as z}from"../aws/organisations/trustedAccess.js";import{enableIpamDelegatedAdmin as G}from"../aws/organisations/ipam.js";import{updateBackupGlobalSettings as K}from"../aws/organisations/backup.js";import{listAccounts as q,createAccount as H}from"../aws/organisations/accounts.js";import{ensureOrganisationalUnitsExist as J,placeAccountsInOUs as Q,buildAccountToOUMap as V}from"../aws/organisations/organisationalUnits.js";import{activateCostAllocationTags as X}from"../aws/organisations/costAllocation.js";import{checkIdentityCentreStatus as Y}from"../aws/organisations/identityCentre.js";import{registerSecurityDelegates as Z}from"../aws/organisations/delegatedAdmin.js";async function Pe(r,o,e){const n=[],s=[],t=[],c=[];let C;const u=r.getClient(v),A=r.getClient(x),S=r.getClient(T),E=r.getClient(M),I=r.getClient(D),w=r.getClient(N),O=r.getClient(B);e?.onPhaseStart?.("create-organisation"),e?.onProgress?.("Ensuring AWS Organisation exists");const p=await F(u);if(!p.success)return e?.onError?.("create-organisation",p.error),e?.onPhaseComplete?.("create-organisation","error"),h(p.error);const{orgId:R,rootId:f}=p.data;if(e?.onPhaseComplete?.("create-organisation","completed"),n.push("create-organisation"),await a("enable-policies",()=>(e?.onProgress?.("Enabling organisation policy types"),L(u,f)),n,t,e),await a("enable-service-access",()=>(e?.onProgress?.("Enabling AWS service access"),W(u)),n,t,e),await a("enable-ram-sharing",()=>(e?.onProgress?.("Enabling RAM sharing"),j(A)),n,t,e),await a("activate-trusted-access",()=>(e?.onProgress?.("Activating CloudFormation trusted access"),z(S)),n,t,e),o.platformAccountId){const i=o.platformAccountId;await a("enable-ipam",()=>(e?.onProgress?.("Enabling IPAM delegated administrator"),G(E,i)),n,t,e)}await a("configure-backup",()=>(e?.onProgress?.("Updating backup global settings"),K(I)),n,t,e),e?.onPhaseStart?.("create-accounts"),e?.onProgress?.("Checking for missing accounts");const d=await _(u,o.accounts,c);d.success?(n.push("create-accounts"),e?.onPhaseComplete?.("create-accounts","completed")):(t.push({phase:"create-accounts",error:d.error.message}),e?.onError?.("create-accounts",d.error),e?.onPhaseComplete?.("create-accounts","error"));let m={};e?.onPhaseStart?.("create-organisational-units"),e?.onProgress?.("Ensuring organisational units exist");const g=await J(u,f,o.organisationalUnits);if(g.success?(m=g.data,n.push("create-organisational-units"),e?.onPhaseComplete?.("create-organisational-units","completed")):(t.push({phase:"create-organisational-units",error:g.error.message}),e?.onError?.("create-organisational-units",g.error),e?.onPhaseComplete?.("create-organisational-units","error")),Object.keys(m).length===0)s.push("place-accounts"),e?.onPhaseStart?.("place-accounts"),e?.onPhaseComplete?.("place-accounts","skipped");else if(o.accountPlacements===void 0){const i=new Error("Account placements not provided despite OUs being created. Caller must populate accountPlacements so accounts can be moved into their target OUs.");t.push({phase:"place-accounts",error:i.message}),e?.onPhaseStart?.("place-accounts"),e?.onError?.("place-accounts",i),e?.onPhaseComplete?.("place-accounts","error")}else if(o.accountPlacements.length===0)s.push("place-accounts"),e?.onPhaseStart?.("place-accounts"),e?.onPhaseComplete?.("place-accounts","skipped");else{const i=o.accountPlacements,U=Array.isArray(o.organisationalUnits)?void 0:V(o.organisationalUnits,m);await a("place-accounts",()=>(e?.onProgress?.("Placing accounts in organisational units"),Q(u,m,i,U)),n,t,e)}const P=o.costAllocationTags??[];if(P.length>0?await a("activate-cost-tags",()=>(e?.onProgress?.("Activating cost allocation tags"),X(w,P.map(i=>({TagKey:i})))),n,t,e):(s.push("activate-cost-tags"),e?.onPhaseStart?.("activate-cost-tags"),e?.onPhaseComplete?.("activate-cost-tags","skipped")),o.skipIdentityCentre)s.push("check-identity-centre"),e?.onPhaseStart?.("check-identity-centre"),e?.onPhaseComplete?.("check-identity-centre","skipped");else{e?.onPhaseStart?.("check-identity-centre"),e?.onProgress?.("Checking Identity Centre status");const i=await Y(O);i.success?(C=i.data.enabled?"enabled":"not-enabled",n.push("check-identity-centre"),e?.onPhaseComplete?.("check-identity-centre","completed")):(t.push({phase:"check-identity-centre",error:i.error.message}),e?.onError?.("check-identity-centre",i.error),e?.onPhaseComplete?.("check-identity-centre","error"))}const l=o.securityDelegateAccountId;return l?await a("register-security-delegates",()=>(e?.onProgress?.("Registering security service delegated administrators"),Z(u,l)),n,t,e):(s.push("register-security-delegates"),e?.onPhaseStart?.("register-security-delegates"),e?.onPhaseComplete?.("register-security-delegates","skipped")),y({organisationId:R,createdAccounts:c,identityCentreStatus:C,phasesCompleted:n,phasesSkipped:s,errors:t})}async function a(r,o,e,n,s){s?.onPhaseStart?.(r);const t=await o();t.success?(e.push(r),s?.onPhaseComplete?.(r,"completed")):(n.push({phase:r,error:t.error.message}),s?.onError?.(r,t.error),s?.onPhaseComplete?.(r,"error"))}async function _(r,o,e){const n=await q(r);if(!n.success)return h(n.error);const s=new Set(n.data.map(t=>t.Name?.toLowerCase()).filter(t=>t!==void 0));for(const t of o){if(s.has(t.name.toLowerCase()))continue;const c=await H(r,t.name,t.email);if(!c.success)return h(c.error);e.push({name:c.data.accountName,accountId:c.data.accountId})}return y(void 0)}export{Pe as runOrganisationSetup};
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@fjall/deploy-core",
3
- "version": "2.4.5",
3
+ "version": "2.4.8",
4
4
  "description": "Shared deployment engine for Fjall — used by CLI and webapp worker",
5
5
  "type": "module",
6
6
  "main": "dist/src/index.js",
@@ -73,7 +73,7 @@
73
73
  "@aws-sdk/client-s3": "^3.1038.0",
74
74
  "@aws-sdk/client-sso-admin": "^3.1038.0",
75
75
  "@aws-sdk/client-sts": "^3.1038.0",
76
- "@fjall/generator": "^2.4.5",
76
+ "@fjall/generator": "^2.4.8",
77
77
  "@fjall/util": "^0.100.0",
78
78
  "@smithy/node-http-handler": "^4.6.1",
79
79
  "zod": "^4.4.3"
@@ -82,5 +82,5 @@
82
82
  "@types/node": "^25.6.0",
83
83
  "vitest": "^4.1.5"
84
84
  },
85
- "gitHead": "0db65703fb07ea2b40ae39c2108e0a1417ade390"
85
+ "gitHead": "a1e1f954e39ecbe0360ac6124dba4146aa747f36"
86
86
  }