@fjall/deploy-core 2.34.0 → 3.1.0

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.
Files changed (209) hide show
  1. package/dist/.minified +1 -1
  2. package/dist/src/aws/SimpleAwsProvider.js +1 -1
  3. package/dist/src/aws/cloudtrail/orgTrailDelivery.js +1 -1
  4. package/dist/src/aws/organisations/accountGlobals.js +1 -1
  5. package/dist/src/aws/organisations/accounts.js +1 -1
  6. package/dist/src/aws/organisations/backup.js +2 -2
  7. package/dist/src/aws/organisations/costAllocation.js +1 -1
  8. package/dist/src/aws/organisations/delegatedAdmin.js +2 -2
  9. package/dist/src/aws/organisations/identityCentre.js +1 -1
  10. package/dist/src/aws/organisations/identityCentreSnapshot.js +1 -1
  11. package/dist/src/aws/organisations/importedAccounts.js +1 -1
  12. package/dist/src/aws/organisations/ipam.js +1 -1
  13. package/dist/src/aws/organisations/oidcProvider.js +1 -1
  14. package/dist/src/aws/organisations/organisation.js +1 -1
  15. package/dist/src/aws/organisations/organisationalUnits.js +1 -1
  16. package/dist/src/aws/organisations/pagination.js +1 -1
  17. package/dist/src/aws/organisations/policies.js +1 -1
  18. package/dist/src/aws/organisations/ram.js +1 -1
  19. package/dist/src/aws/organisations/rootAccess.js +3 -3
  20. package/dist/src/aws/organisations/serviceAccess.js +1 -1
  21. package/dist/src/aws/organisations/trustedAccess.js +1 -1
  22. package/dist/src/aws/organisations/types.js +1 -1
  23. package/dist/src/aws/regions/regionOptStatus.js +1 -1
  24. package/dist/src/aws/sts/assumeRoot.js +1 -1
  25. package/dist/src/aws/targetReadiness.d.ts +7 -4
  26. package/dist/src/aws/targetReadiness.js +1 -1
  27. package/dist/src/aws/targetSetAdvisory.js +1 -1
  28. package/dist/src/aws/utils/arnParser.js +1 -1
  29. package/dist/src/aws/utils/awsErrorHandler.js +1 -1
  30. package/dist/src/aws/utils/cloudformationEventHelpers.js +1 -1
  31. package/dist/src/aws/utils/cloudformationEvents.js +1 -1
  32. package/dist/src/index.d.ts +4 -2
  33. package/dist/src/index.js +1 -1
  34. package/dist/src/orchestration/activeDeploymentGuard.js +5 -5
  35. package/dist/src/orchestration/application/applicationDeploy.js +1 -1
  36. package/dist/src/orchestration/application/applicationDeployHelpers.js +3 -3
  37. package/dist/src/orchestration/application/applicationDestroy.js +1 -1
  38. package/dist/src/orchestration/application/approvalGate.js +1 -1
  39. package/dist/src/orchestration/application/artefactCollection.js +1 -1
  40. package/dist/src/orchestration/application/buildArgResolver.js +1 -1
  41. package/dist/src/orchestration/application/buildGroupValidator.js +1 -1
  42. package/dist/src/orchestration/application/buildSecretResolver.js +1 -1
  43. package/dist/src/orchestration/application/buildSecretSession.js +1 -1
  44. package/dist/src/orchestration/application/codeOnlyDeploy.js +1 -1
  45. package/dist/src/orchestration/application/databaseEndpointReconcile.js +1 -1
  46. package/dist/src/orchestration/application/deploySessionPolicy.js +1 -1
  47. package/dist/src/orchestration/application/destroyPreFlight.js +1 -1
  48. package/dist/src/orchestration/application/destructionGate.js +3 -3
  49. package/dist/src/orchestration/application/detectionPipeline.js +1 -1
  50. package/dist/src/orchestration/application/dockerBuildHelper.js +1 -1
  51. package/dist/src/orchestration/application/ecrCacheRepository.js +1 -1
  52. package/dist/src/orchestration/application/lambdaCodeOnlyRollout.js +1 -1
  53. package/dist/src/orchestration/application/leaseGate.js +1 -1
  54. package/dist/src/orchestration/application/openNextBuild.js +3 -3
  55. package/dist/src/orchestration/application/plan/approvalToken.js +1 -1
  56. package/dist/src/orchestration/application/plan/assemblyDigest.js +2 -2
  57. package/dist/src/orchestration/application/plan/buildDeployPlan.js +1 -1
  58. package/dist/src/orchestration/application/plan/changeSetEscalation.js +1 -1
  59. package/dist/src/orchestration/application/plan/classify.js +1 -1
  60. package/dist/src/orchestration/application/plan/computeDeployPlan.js +1 -1
  61. package/dist/src/orchestration/application/plan/loadDeployPlan.js +1 -1
  62. package/dist/src/orchestration/application/plan/pinnedNames.js +1 -1
  63. package/dist/src/orchestration/application/plan/registryOverlay.js +1 -1
  64. package/dist/src/orchestration/application/plan/renderDeployPlan.js +1 -1
  65. package/dist/src/orchestration/application/serviceImageTagsRollback.js +1 -1
  66. package/dist/src/orchestration/application/taskDefinitionRoll.js +1 -1
  67. package/dist/src/orchestration/application/welcomeImageHelper.js +1 -1
  68. package/dist/src/orchestration/builders/dockerBuilder.js +1 -1
  69. package/dist/src/orchestration/builders/frameworkRegistry.js +1 -1
  70. package/dist/src/orchestration/builders/openNextBuilder.js +1 -1
  71. package/dist/src/orchestration/builders/staticSiteBuilder.js +2 -2
  72. package/dist/src/orchestration/contextHelpers.js +1 -1
  73. package/dist/src/orchestration/deploy.js +1 -1
  74. package/dist/src/orchestration/destroy.js +1 -1
  75. package/dist/src/orchestration/domain/caaPreflight.d.ts +56 -0
  76. package/dist/src/orchestration/domain/caaPreflight.js +1 -0
  77. package/dist/src/orchestration/domain/delegatedDomainDeploy.d.ts +79 -0
  78. package/dist/src/orchestration/domain/delegatedDomainDeploy.js +1 -0
  79. package/dist/src/orchestration/domain/delegationDestroyMirror.d.ts +141 -0
  80. package/dist/src/orchestration/domain/delegationDestroyMirror.js +1 -0
  81. package/dist/src/orchestration/domain/index.d.ts +10 -0
  82. package/dist/src/orchestration/domain/index.js +1 -0
  83. package/dist/src/orchestration/domain/nsPropagationGate.d.ts +59 -0
  84. package/dist/src/orchestration/domain/nsPropagationGate.js +1 -0
  85. package/dist/src/orchestration/domain/types.d.ts +75 -0
  86. package/dist/src/orchestration/domain/types.js +1 -0
  87. package/dist/src/orchestration/domainInterface.d.ts +10 -0
  88. package/dist/src/orchestration/drift/classifyDriftFailure.js +1 -1
  89. package/dist/src/orchestration/drift/detectStackDrift.js +1 -1
  90. package/dist/src/orchestration/drift/driftPreFlight.js +5 -5
  91. package/dist/src/orchestration/drift/driftProbe.d.ts +12 -6
  92. package/dist/src/orchestration/drift/driftProbe.js +1 -1
  93. package/dist/src/orchestration/drift/driftSuspectJournal.js +1 -1
  94. package/dist/src/orchestration/drift/preFlightGate.d.ts +10 -2
  95. package/dist/src/orchestration/drift/preFlightGate.js +2 -2
  96. package/dist/src/orchestration/drift/route53Names.d.ts +35 -0
  97. package/dist/src/orchestration/drift/route53Names.js +1 -0
  98. package/dist/src/orchestration/drift/route53RecordPreflight.d.ts +105 -0
  99. package/dist/src/orchestration/drift/route53RecordPreflight.js +2 -0
  100. package/dist/src/orchestration/index.d.ts +3 -1
  101. package/dist/src/orchestration/index.js +1 -1
  102. package/dist/src/orchestration/manifestSecretParser.js +1 -1
  103. package/dist/src/orchestration/organisation/accountsConfig.js +1 -1
  104. package/dist/src/orchestration/organisation/cascadeDestroyHelpers.js +1 -1
  105. package/dist/src/orchestration/organisation/cascadeHelpers.d.ts +18 -1
  106. package/dist/src/orchestration/organisation/cascadeHelpers.js +1 -1
  107. package/dist/src/orchestration/organisation/cascadeSummary.js +1 -1
  108. package/dist/src/orchestration/organisation/organisationDeploy.js +1 -1
  109. package/dist/src/orchestration/organisation/organisationDestroy.js +2 -2
  110. package/dist/src/orchestration/organisation/organisationSetup.js +1 -1
  111. package/dist/src/orchestration/organisation/reconcileProviderAccounts.js +1 -1
  112. package/dist/src/orchestration/organisationDeploy/cascadeExecution.js +1 -1
  113. package/dist/src/orchestration/organisationDeploy/infraSteps.js +1 -1
  114. package/dist/src/orchestration/organisationDeploy/orgCascadeDeploy.js +3 -3
  115. package/dist/src/orchestration/organisationDeploy/orgContext.js +1 -1
  116. package/dist/src/orchestration/organisationDeploy/resolveCascadeAccounts.js +1 -1
  117. package/dist/src/orchestration/organisationDeploy/singleComponentDeploy.js +1 -1
  118. package/dist/src/orchestration/organisationDeploy/trailReconciliation.js +1 -1
  119. package/dist/src/orchestration/probes/deleteBlockProbes.js +1 -1
  120. package/dist/src/orchestration/remediation/driftRepairGate.js +1 -1
  121. package/dist/src/orchestration/remediation/forensicsCapture.js +1 -1
  122. package/dist/src/orchestration/remediation/forgetResource.js +1 -1
  123. package/dist/src/orchestration/remediation/pinRemediation.js +1 -1
  124. package/dist/src/orchestration/remediation/postFailureRepairOffer.js +8 -8
  125. package/dist/src/orchestration/remediation/recreatePreFlight.js +1 -1
  126. package/dist/src/orchestration/remediation/recreateResource.js +1 -1
  127. package/dist/src/orchestration/remediation/remediationJournal.js +2 -2
  128. package/dist/src/orchestration/remediation/removalUpdate.js +1 -1
  129. package/dist/src/orchestration/remediation/retainFlip.js +1 -1
  130. package/dist/src/orchestration/resolveOperation.js +1 -1
  131. package/dist/src/orchestration/restart/restartApplication.js +1 -1
  132. package/dist/src/orchestration/restart/secretsDrift.js +1 -1
  133. package/dist/src/orchestration/secretArnResolver.js +1 -1
  134. package/dist/src/orchestration/serviceFactory.js +1 -1
  135. package/dist/src/orchestration/spawnHelpers.js +1 -1
  136. package/dist/src/orchestration/stackCleanup/bucketOps.js +1 -1
  137. package/dist/src/orchestration/stackCleanup/failedStack.js +1 -1
  138. package/dist/src/orchestration/stackCleanup/logging.js +1 -1
  139. package/dist/src/orchestration/stackCleanup/messages.js +1 -1
  140. package/dist/src/orchestration/stackCleanup/orphanSweep.js +1 -1
  141. package/dist/src/orchestration/stackCleanup/preEmpty.js +1 -1
  142. package/dist/src/orchestration/stackCleanup/stackResources.js +1 -1
  143. package/dist/src/orchestration/stepLifecycle.js +1 -1
  144. package/dist/src/orchestration/trailMigration/memberTrailCleanup.js +1 -1
  145. package/dist/src/orchestration/trailMigration/trailMigration.js +1 -1
  146. package/dist/src/orchestration/unlock/bucketPolicyTriage.js +1 -1
  147. package/dist/src/orchestration/unlock/restoreAndReconcileQuarantinedBucket.js +1 -1
  148. package/dist/src/orchestration/unlock/restoreBucketPolicy.js +1 -1
  149. package/dist/src/orchestration/unlock/scpRemediation.js +1 -1
  150. package/dist/src/orchestration/unlock/toResourcePolicyStatements.js +1 -1
  151. package/dist/src/orchestration/unlock/unlockBucket.js +1 -1
  152. package/dist/src/orchestration/unlock/unlockQueue.js +1 -1
  153. package/dist/src/services/application/ApplicationStackService.d.ts +7 -0
  154. package/dist/src/services/application/ApplicationStackService.js +1 -1
  155. package/dist/src/services/application/applicationStackHelpers.js +3 -3
  156. package/dist/src/services/infrastructure/CdkArgumentBuilder.js +1 -1
  157. package/dist/src/services/infrastructure/CdkCommandRunner.js +2 -2
  158. package/dist/src/services/infrastructure/CdkErrorFormatter.js +10 -10
  159. package/dist/src/services/infrastructure/CdkEventMonitoring.js +1 -1
  160. package/dist/src/services/infrastructure/CdkOutputAnalyser.js +2 -2
  161. package/dist/src/services/infrastructure/CdkOutputParser.js +2 -2
  162. package/dist/src/services/infrastructure/CdkProcessManager.js +3 -3
  163. package/dist/src/services/infrastructure/CdkService.d.ts +1 -1
  164. package/dist/src/services/infrastructure/CdkService.js +2 -2
  165. package/dist/src/services/infrastructure/CdkServiceTypes.d.ts +39 -0
  166. package/dist/src/services/infrastructure/CfnRegistryService.js +1 -1
  167. package/dist/src/services/infrastructure/CloudFormationService.js +1 -1
  168. package/dist/src/services/infrastructure/EcrImageInspectorService.js +1 -1
  169. package/dist/src/services/infrastructure/EcsService.js +1 -1
  170. package/dist/src/services/infrastructure/EcsServiceResolver.js +1 -1
  171. package/dist/src/services/infrastructure/LambdaService.js +1 -1
  172. package/dist/src/services/infrastructure/SsmParameterMetadataService.js +1 -1
  173. package/dist/src/services/infrastructure/cancelInFlightStack.js +3 -3
  174. package/dist/src/services/infrastructure/cdkServiceHelpers.js +1 -1
  175. package/dist/src/services/infrastructure/changeSetProbe.js +1 -1
  176. package/dist/src/services/infrastructure/constructMapEnrichment.js +1 -1
  177. package/dist/src/services/supporting/CdkContextBuilder.d.ts +1 -0
  178. package/dist/src/services/supporting/CdkContextBuilder.js +1 -1
  179. package/dist/src/services/supporting/TemplateHashService.js +1 -1
  180. package/dist/src/services/supporting/helpers.js +1 -1
  181. package/dist/src/steps/stepRegistry.js +1 -1
  182. package/dist/src/types/ProgressEvent.js +1 -1
  183. package/dist/src/types/application/ApplicationServiceTypes.js +1 -1
  184. package/dist/src/types/callbacks.js +1 -1
  185. package/dist/src/types/config/FjallState.js +1 -1
  186. package/dist/src/types/constants.js +1 -1
  187. package/dist/src/types/deployEmitter.js +1 -1
  188. package/dist/src/types/deployEvent.d.ts +1 -0
  189. package/dist/src/types/deployEvent.js +1 -1
  190. package/dist/src/types/deployEventParse.js +1 -1
  191. package/dist/src/types/deployment/DeploymentServiceTypes.js +1 -1
  192. package/dist/src/types/deployment/DeploymentTypes.d.ts +1 -0
  193. package/dist/src/types/deploymentEventSchema.d.ts +1 -0
  194. package/dist/src/types/deploymentEventSchema.js +1 -1
  195. package/dist/src/types/detection/patternDetection.js +1 -1
  196. package/dist/src/types/detection/patternTypes.d.ts +9 -0
  197. package/dist/src/types/drift.js +1 -1
  198. package/dist/src/types/errors/CdkError.js +1 -1
  199. package/dist/src/types/errors/ServiceError.js +1 -1
  200. package/dist/src/types/operations.d.ts +26 -2
  201. package/dist/src/types/operations.js +1 -1
  202. package/dist/src/types/params.d.ts +20 -0
  203. package/dist/src/types/remediation.js +1 -1
  204. package/dist/src/types/stepDefinitions.d.ts +3 -0
  205. package/dist/src/types/stepDefinitions.js +1 -1
  206. package/dist/src/util/dockerfileDetection.js +1 -1
  207. package/dist/src/util/sequencedCallbacks.js +1 -1
  208. package/dist/src/util/sleepAbortable.js +1 -1
  209. package/package.json +5 -4
package/dist/.minified CHANGED
@@ -1 +1 @@
1
- 218 files minified at 2026-07-17T22:47:38.950Z
1
+ 226 files minified at 2026-07-18T06:59:22.431Z
@@ -1 +1 @@
1
- import{STSClient as r,AssumeRoleCommand as d}from"@aws-sdk/client-sts";import{NodeHttpHandler as a}from"@smithy/node-http-handler";import{composeSdkAbortSignal as S}from"./organisations/types.js";const o=3e4;class i{credentials;region;accountId;clientCache=new Map;assumeScopedBuildSecretSession;constructor(e,n){this.credentials={accessKeyId:e.accessKeyId,secretAccessKey:e.secretAccessKey,sessionToken:e.sessionToken},this.region=e.region,this.accountId=e.accountId,n!==void 0&&(this.assumeScopedBuildSecretSession=async(t,c)=>{const s=await n(t,c);return new i(s)})}getClient(e){const n=e.name,t=this.clientCache.get(n);if(t!==void 0)return t;const c={region:this.region,credentials:this.credentials,requestHandler:new a({requestTimeout:o})},s=new e(c);return this.clientCache.set(n,s),s}getRegion(){return this.region}getAccountId(){return this.accountId}getCredentials(){return this.credentials}exportToEnv(){process.env.AWS_ACCESS_KEY_ID=this.credentials.accessKeyId,process.env.AWS_SECRET_ACCESS_KEY=this.credentials.secretAccessKey,process.env.AWS_REGION=this.region,this.credentials.sessionToken?process.env.AWS_SESSION_TOKEN=this.credentials.sessionToken:delete process.env.AWS_SESSION_TOKEN}async assumeRole(e,n){const s=(await this.getClient(r).send(new d({RoleArn:e,RoleSessionName:n}),{abortSignal:S(void 0,o)})).Credentials;if(!s?.AccessKeyId||!s?.SecretAccessKey)throw new Error(`AssumeRole for ${e} returned incomplete credentials`);return{accessKeyId:s.AccessKeyId,secretAccessKey:s.SecretAccessKey,sessionToken:s.SessionToken}}}export{i as SimpleAwsProvider};
1
+ var a=Object.defineProperty;var o=(d,e)=>a(d,"name",{value:e,configurable:!0});import{STSClient as S,AssumeRoleCommand as l}from"@aws-sdk/client-sts";import{NodeHttpHandler as u}from"@smithy/node-http-handler";import{composeSdkAbortSignal as h}from"./organisations/types.js";const i=3e4;class r{static{o(this,"SimpleAwsProvider")}credentials;region;accountId;clientCache=new Map;assumeScopedBuildSecretSession;constructor(e,n){this.credentials={accessKeyId:e.accessKeyId,secretAccessKey:e.secretAccessKey,sessionToken:e.sessionToken},this.region=e.region,this.accountId=e.accountId,n!==void 0&&(this.assumeScopedBuildSecretSession=async(t,c)=>{const s=await n(t,c);return new r(s)})}getClient(e){const n=e.name,t=this.clientCache.get(n);if(t!==void 0)return t;const c={region:this.region,credentials:this.credentials,requestHandler:new u({requestTimeout:i})},s=new e(c);return this.clientCache.set(n,s),s}getRegion(){return this.region}getAccountId(){return this.accountId}getCredentials(){return this.credentials}exportToEnv(){process.env.AWS_ACCESS_KEY_ID=this.credentials.accessKeyId,process.env.AWS_SECRET_ACCESS_KEY=this.credentials.secretAccessKey,process.env.AWS_REGION=this.region,this.credentials.sessionToken?process.env.AWS_SESSION_TOKEN=this.credentials.sessionToken:delete process.env.AWS_SESSION_TOKEN}async assumeRole(e,n){const s=(await this.getClient(S).send(new l({RoleArn:e,RoleSessionName:n}),{abortSignal:h(void 0,i)})).Credentials;if(!s?.AccessKeyId||!s?.SecretAccessKey)throw new Error(`AssumeRole for ${e} returned incomplete credentials`);return{accessKeyId:s.AccessKeyId,secretAccessKey:s.SecretAccessKey,sessionToken:s.SessionToken}}}export{r as SimpleAwsProvider};
@@ -1 +1 @@
1
- import{ListObjectsV2Command as w}from"@aws-sdk/client-s3";import{GetTrailStatusCommand as P}from"@aws-sdk/client-cloudtrail";import{success as T,failure as S}from"@fjall/generator";import{getErrorMessage as E,maskSensitiveOutput as M}from"@fjall/util";import{composeSdkAbortSignal as b,extractErrorName as k,isAborted as x}from"../organisations/types.js";const d=1440*60*1e3,D=d,v=50,A=2;function N(a,e){const n=[],t=Math.floor((e-a)/d)*d;for(let c=t;c<=e;c+=d){const o=new Date(c),s=String(o.getUTCMonth()+1).padStart(2,"0"),r=String(o.getUTCDate()).padStart(2,"0");n.push(`${o.getUTCFullYear()}/${s}/${r}`)}return n.reverse()}async function L(a,e){let n=!1,t,c=!1,o=!1;const s=i=>{for(const u of i.Contents??[])n=!0,u.LastModified!==void 0&&((t===void 0||u.LastModified>t)&&(t=u.LastModified),u.LastModified.getTime()>=e.recentSinceMs&&(c=!0))},r=i=>({delivered:n,recentDelivery:i,...t!==void 0?{latestObjectAt:t}:{}}),f=await a.send(new w({Bucket:e.bucketName,Prefix:e.accountPrefix,Delimiter:"/"}),{abortSignal:b(e.abortSignal)});s(f);const y=(f.CommonPrefixes??[]).map(i=>i.Prefix).filter(i=>i!==void 0);if(f.IsTruncated===!0||y.length>v)return r(void 0);for(const i of y)for(const u of e.datePaths){let l,g=!1;for(let h=0;h<A&&!g;h++){if(x(e.abortSignal))throw new Error("Aborted: org-trail delivery scan cancelled by shutdown signal");const m=await a.send(new w({Bucket:e.bucketName,Prefix:`${i}${u}/`,...l!==void 0?{ContinuationToken:l}:{}}),{abortSignal:b(e.abortSignal)});if(s(m),c)return r(!0);m.IsTruncated===!0?l=m.NextContinuationToken:g=!0}g||(o=!0)}return r(o?void 0:!1)}async function F(a,e){const n=e.recencyWindowMs??D,t=Date.now(),c=N(n,t),o=[];for(const s of e.accountIds)try{const r=await L(a,{bucketName:e.bucketName,accountPrefix:`AWSLogs/${e.orgId}/${s}/CloudTrail/`,datePaths:c,recentSinceMs:t-n,...e.abortSignal!==void 0?{abortSignal:e.abortSignal}:{}});o.push({accountId:s,...r})}catch(r){return k(r)==="NoSuchBucket"?S(new Error(`Organisation trail bucket ${e.bucketName} does not exist \u2014 the org trail has not been deployed yet.`)):S(new Error(`Failed to verify org-trail delivery for account ${s}: ${M(E(r))}`))}return T({bucketName:e.bucketName,perAccount:o})}async function O(a,e,n){try{const t=await a.send(new P({Name:e}),{abortSignal:b(n)});return T({isLogging:t.IsLogging===!0,...t.LatestDeliveryTime!==void 0?{latestDeliveryTime:t.LatestDeliveryTime}:{}})}catch(t){return S(new Error(`Failed to read trail status for ${e}: ${M(E(t))}`))}}export{O as getTrailLoggingStatus,F as verifyOrgTrailDelivery};
1
+ var k=Object.defineProperty;var u=(n,e)=>k(n,"name",{value:e,configurable:!0});import{ListObjectsV2Command as T}from"@aws-sdk/client-s3";import{GetTrailStatusCommand as x}from"@aws-sdk/client-cloudtrail";import{success as E,failure as b}from"@fjall/generator";import{getErrorMessage as M,maskSensitiveOutput as P}from"@fjall/util";import{composeSdkAbortSignal as y,extractErrorName as D,isAborted as v}from"../organisations/types.js";const f=1440*60*1e3,A=f,N=50,L=2;function $(n,e){const r=[],t=Math.floor((e-n)/f)*f;for(let c=t;c<=e;c+=f){const i=new Date(c),s=String(i.getUTCMonth()+1).padStart(2,"0"),o=String(i.getUTCDate()).padStart(2,"0");r.push(`${i.getUTCFullYear()}/${s}/${o}`)}return r.reverse()}u($,"utcDatePathsForWindow");async function C(n,e){let r=!1,t,c=!1,i=!1;const s=u(a=>{for(const d of a.Contents??[])r=!0,d.LastModified!==void 0&&((t===void 0||d.LastModified>t)&&(t=d.LastModified),d.LastModified.getTime()>=e.recentSinceMs&&(c=!0))},"ingest"),o=u(a=>({delivered:r,recentDelivery:a,...t!==void 0?{latestObjectAt:t}:{}}),"outcome"),l=await n.send(new T({Bucket:e.bucketName,Prefix:e.accountPrefix,Delimiter:"/"}),{abortSignal:y(e.abortSignal)});s(l);const h=(l.CommonPrefixes??[]).map(a=>a.Prefix).filter(a=>a!==void 0);if(l.IsTruncated===!0||h.length>N)return o(void 0);for(const a of h)for(const d of e.datePaths){let g,m=!1;for(let w=0;w<L&&!m;w++){if(v(e.abortSignal))throw new Error("Aborted: org-trail delivery scan cancelled by shutdown signal");const S=await n.send(new T({Bucket:e.bucketName,Prefix:`${a}${d}/`,...g!==void 0?{ContinuationToken:g}:{}}),{abortSignal:y(e.abortSignal)});if(s(S),c)return o(!0);S.IsTruncated===!0?g=S.NextContinuationToken:m=!0}m||(i=!0)}return o(i?void 0:!1)}u(C,"scanAccountDelivery");async function R(n,e){const r=e.recencyWindowMs??A,t=Date.now(),c=$(r,t),i=[];for(const s of e.accountIds)try{const o=await C(n,{bucketName:e.bucketName,accountPrefix:`AWSLogs/${e.orgId}/${s}/CloudTrail/`,datePaths:c,recentSinceMs:t-r,...e.abortSignal!==void 0?{abortSignal:e.abortSignal}:{}});i.push({accountId:s,...o})}catch(o){return D(o)==="NoSuchBucket"?b(new Error(`Organisation trail bucket ${e.bucketName} does not exist \u2014 the org trail has not been deployed yet.`)):b(new Error(`Failed to verify org-trail delivery for account ${s}: ${P(M(o))}`))}return E({bucketName:e.bucketName,perAccount:i})}u(R,"verifyOrgTrailDelivery");async function U(n,e,r){try{const t=await n.send(new x({Name:e}),{abortSignal:y(r)});return E({isLogging:t.IsLogging===!0,...t.LatestDeliveryTime!==void 0?{latestDeliveryTime:t.LatestDeliveryTime}:{}})}catch(t){return b(new Error(`Failed to read trail status for ${e}: ${P(M(t))}`))}}u(U,"getTrailLoggingStatus");export{U as getTrailLoggingStatus,R as verifyOrgTrailDelivery};
@@ -1 +1 @@
1
- import{GetRoleCommand as i}from"@aws-sdk/client-iam";import{success as n,failure as a}from"@fjall/generator";import{accountTier as c,getErrorMessage as s}from"@fjall/util";import{ACCOUNT_MONITORING_ROLE_NAME as l}from"@fjall/util/aws";import{composeSdkAbortSignal as u}from"./types.js";const t=l;async function f(o,e){try{return await o.send(new i({RoleName:t}),{abortSignal:u(e)}),n(!0)}catch(r){return r instanceof Error&&r.name==="NoSuchEntityException"?n(!1):a(new Error(`Failed to probe account globals (role "${t}"): ${s(r)}`))}}function p(o){return(o??[]).some(e=>c(e)==="organisation")}function A(o){const r=p(o.providerAccounts)?`run \`fjall deploy organisation\` to provision the primary region (${o.primaryRegion}), then retry this deploy`:`target the primary region (${o.primaryRegion}) and run \`fjall deploy account\`, then retry this deploy`;return`Cannot deploy "${o.target}" into ${o.deployRegion}: the account globals (fixed-name IAM resources such as the ${t} role) are created only in the primary region (${o.primaryRegion}) and were not found in this account. Nothing has been deployed. Deploy the primary region first: ${r}.`}export{t as ACCOUNT_GLOBALS_ROLE_NAME,A as buildMissingAccountGlobalsAdvisory,f as describeAccountGlobalsExist,p as hasOrganisationTierAccount};
1
+ var a=Object.defineProperty;var t=(o,r)=>a(o,"name",{value:r,configurable:!0});import{GetRoleCommand as c}from"@aws-sdk/client-iam";import{success as i,failure as s}from"@fjall/generator";import{accountTier as l,getErrorMessage as u}from"@fjall/util";import{ACCOUNT_MONITORING_ROLE_NAME as p}from"@fjall/util/aws";import{composeSdkAbortSignal as g}from"./types.js";const n=p;async function E(o,r){try{return await o.send(new c({RoleName:n}),{abortSignal:g(r)}),i(!0)}catch(e){return e instanceof Error&&e.name==="NoSuchEntityException"?i(!1):s(new Error(`Failed to probe account globals (role "${n}"): ${u(e)}`))}}t(E,"describeAccountGlobalsExist");function d(o){return(o??[]).some(r=>l(r)==="organisation")}t(d,"hasOrganisationTierAccount");function O(o){const e=d(o.providerAccounts)?`run \`fjall deploy organisation\` to provision the primary region (${o.primaryRegion}), then retry this deploy`:`target the primary region (${o.primaryRegion}) and run \`fjall deploy account\`, then retry this deploy`;return`Cannot deploy "${o.target}" into ${o.deployRegion}: the account globals (fixed-name IAM resources such as the ${n} role) are created only in the primary region (${o.primaryRegion}) and were not found in this account. Nothing has been deployed. Deploy the primary region first: ${e}.`}t(O,"buildMissingAccountGlobalsAdvisory");export{n as ACCOUNT_GLOBALS_ROLE_NAME,O as buildMissingAccountGlobalsAdvisory,E as describeAccountGlobalsExist,d as hasOrganisationTierAccount};
@@ -1 +1 @@
1
- import{ListAccountsCommand as E,CreateAccountCommand as h,CreateAccountState as p,DescribeCreateAccountStatusCommand as S}from"@aws-sdk/client-organizations";import{success as A,failure as e}from"@fjall/generator";import{getErrorMessage as m}from"@fjall/util";import{extractErrorName as g,composeSdkAbortSignal as f,isAborted as w,AWS_ERROR_NAMES as C}from"./types.js";import{sleepAbortable as $}from"../../util/sleepAbortable.js";async function b(s,r){try{const t=await s.send(new E({MaxResults:20}),{abortSignal:f(r)});let n=t.Accounts??[],c=t.NextToken;for(;c;){if(w(r))return e(new Error("Aborted: account listing cancelled by shutdown signal"));const o=await s.send(new E({MaxResults:20,NextToken:c}),{abortSignal:f(r)});n=n.concat(o.Accounts??[]),c=o.NextToken}return A(n)}catch(t){return g(t)===C.ORGS_NOT_IN_USE?e(new Error("AWS Organisations is not enabled for this account")):e(new Error(`Failed to list accounts: ${m(t)}`))}}async function T(s,r){const t=await b(s);return t.success?A(t.data.find(n=>n.Id===r)):t}async function _(s,r,t,n=180,c=5e3,o){if(w(o))return e(new Error(`Aborted: account creation for "${r}" cancelled by shutdown signal before starting`));try{let u;try{u=await s.send(new h({AccountName:r,Email:t}),{abortSignal:f(o)})}catch(i){const l=g(i);if(l===C.ACCESS_DENIED)return e(new Error(`Access denied when creating account "${r}". Ensure your credentials have organizations:CreateAccount permission.`));if(l==="DuplicateAccountException")return e(new Error(`An account with the email "${t}" already exists in the organisation.`));if(l==="FinalizingOrganizationException")return e(new Error("The organisation is still being initialised. Please wait and try again."));throw i}const d=u.CreateAccountStatus?.Id;if(!d)return e(new Error(`CreateAccount request for "${r}" did not return a status ID`));for(let i=0;i<n;i++){if(w(o))return e(new Error(`Aborted: account creation polling for "${r}" cancelled by shutdown signal \u2014 AWS request ${d} may still complete server-side`));const a=(await s.send(new S({CreateAccountRequestId:d}),{abortSignal:f(o)})).CreateAccountStatus;if(!a)return e(new Error(`No status returned for CreateAccount request ${d}`));if(a.State===p.SUCCEEDED)return a.AccountId?A({accountId:a.AccountId,accountName:r}):e(new Error(`Account creation succeeded but no account ID returned for "${r}"`));if(a.State===p.FAILED)return e(new Error(`Account creation failed for "${r}": ${a.FailureReason}`));await $(c,o)}return e(new Error(`Account creation for "${r}" timed out after ${n*c/1e3} seconds`))}catch(u){return e(new Error(`Failed to create account "${r}": ${m(u)}`))}}export{_ as createAccount,T as findAccount,b as listAccounts};
1
+ var S=Object.defineProperty;var f=(n,r)=>S(n,"name",{value:r,configurable:!0});import{ListAccountsCommand as p,CreateAccountCommand as $,CreateAccountState as m,DescribeCreateAccountStatusCommand as b}from"@aws-sdk/client-organizations";import{success as w,failure as e}from"@fjall/generator";import{getErrorMessage as g}from"@fjall/util";import{extractErrorName as C,composeSdkAbortSignal as A,isAborted as E,AWS_ERROR_NAMES as h}from"./types.js";import{sleepAbortable as x}from"../../util/sleepAbortable.js";async function y(n,r){try{const t=await n.send(new p({MaxResults:20}),{abortSignal:A(r)});let o=t.Accounts??[],c=t.NextToken;for(;c;){if(E(r))return e(new Error("Aborted: account listing cancelled by shutdown signal"));const s=await n.send(new p({MaxResults:20,NextToken:c}),{abortSignal:A(r)});o=o.concat(s.Accounts??[]),c=s.NextToken}return w(o)}catch(t){return C(t)===h.ORGS_NOT_IN_USE?e(new Error("AWS Organisations is not enabled for this account")):e(new Error(`Failed to list accounts: ${g(t)}`))}}f(y,"listAccounts");async function q(n,r){const t=await y(n);return t.success?w(t.data.find(o=>o.Id===r)):t}f(q,"findAccount");async function F(n,r,t,o=180,c=5e3,s){if(E(s))return e(new Error(`Aborted: account creation for "${r}" cancelled by shutdown signal before starting`));try{let u;try{u=await n.send(new $({AccountName:r,Email:t}),{abortSignal:A(s)})}catch(i){const l=C(i);if(l===h.ACCESS_DENIED)return e(new Error(`Access denied when creating account "${r}". Ensure your credentials have organizations:CreateAccount permission.`));if(l==="DuplicateAccountException")return e(new Error(`An account with the email "${t}" already exists in the organisation.`));if(l==="FinalizingOrganizationException")return e(new Error("The organisation is still being initialised. Please wait and try again."));throw i}const d=u.CreateAccountStatus?.Id;if(!d)return e(new Error(`CreateAccount request for "${r}" did not return a status ID`));for(let i=0;i<o;i++){if(E(s))return e(new Error(`Aborted: account creation polling for "${r}" cancelled by shutdown signal \u2014 AWS request ${d} may still complete server-side`));const a=(await n.send(new b({CreateAccountRequestId:d}),{abortSignal:A(s)})).CreateAccountStatus;if(!a)return e(new Error(`No status returned for CreateAccount request ${d}`));if(a.State===m.SUCCEEDED)return a.AccountId?w({accountId:a.AccountId,accountName:r}):e(new Error(`Account creation succeeded but no account ID returned for "${r}"`));if(a.State===m.FAILED)return e(new Error(`Account creation failed for "${r}": ${a.FailureReason}`));await x(c,s)}return e(new Error(`Account creation for "${r}" timed out after ${o*c/1e3} seconds`))}catch(u){return e(new Error(`Failed to create account "${r}": ${g(u)}`))}}f(F,"createAccount");export{F as createAccount,q as findAccount,y as listAccounts};
@@ -1,2 +1,2 @@
1
- import{DescribeBackupVaultCommand as p,ListBackupVaultsCommand as m,UpdateGlobalSettingsCommand as k}from"@aws-sdk/client-backup";import{success as i,failure as c}from"@fjall/generator";import{BACKUP_VAULT_NAME as o,getErrorMessage as u,maskSensitiveOutput as d}from"@fjall/util";import{composeSdkAbortSignal as s,isAborted as f}from"./types.js";const b={enableCrossAccountBackup:!0,enableDelegatedAdministrator:!0,enableMpa:!1};async function B(e,r,t){try{const n={...b,...r},a={isCrossAccountBackupEnabled:n.enableCrossAccountBackup.toString(),isDelegatedAdministratorEnabled:n.enableDelegatedAdministrator.toString(),isMpaEnabled:n.enableMpa.toString()};return await e.send(new k({GlobalSettings:a}),{abortSignal:s(t)}),i(void 0)}catch(n){return c(new Error(`Failed to update backup global settings: ${u(n)}`))}}function v(e,r){return r===void 0||r===""?!1:e==="production"||e==="compliance"}async function N(e,r){try{return await e.send(new p({BackupVaultName:o}),{abortSignal:s(r)}),i(!0)}catch(t){return t instanceof Error&&t.name==="ResourceNotFoundException"?i(!1):t instanceof Error&&t.name==="AccessDeniedException"?g(e,r):c(new Error(`Failed to describe backup vault "${o}": ${d(u(t))}`))}}async function g(e,r){try{let t;do{if(f(r))return c(new Error("Backup vault probe aborted"));const n=await e.send(new m({NextToken:t}),{abortSignal:s(r)});if((n.BackupVaultList??[]).some(l=>l.BackupVaultName===o))return i(!0);t=n.NextToken}while(t!==void 0);return i(!1)}catch(t){return c(new Error(`Failed to list backup vaults probing "${o}": ${d(u(t))}`))}}async function w(e,r,t){try{const n=await e.send(new p({BackupVaultName:o}),{abortSignal:s(t)}),a=n.LockDate,l=a!==void 0&&a.getTime()<=Date.now();return i({vaultName:n.BackupVaultName??o,region:r,locked:n.Locked??!1,...a!==void 0&&{lockDate:a},lockPermanent:l,recoveryPointCount:n.NumberOfRecoveryPoints??0,...n.EncryptionKeyArn!==void 0&&{encryptionKeyArn:n.EncryptionKeyArn}})}catch(n){return n instanceof Error&&n.name==="ResourceNotFoundException"?i(null):c(new Error(`Failed to describe surviving backup vault "${o}": ${d(u(n))}`))}}function x(e){const r=e.locked?e.lockPermanent?"compliance-locked, PERMANENT \u2014 cannot be removed by anyone, including AWS or the root user":e.lockDate!==void 0?`compliance-locked, immutable from ${e.lockDate.toISOString().slice(0,10)}`:"governance-locked \u2014 removable by privileged IAM":"unlocked";return[`Backup vault "${e.vaultName}" in ${e.region} SURVIVES this destroy.`,` \u2022 Lock: ${r}`,` \u2022 Recovery points retained: ${e.recoveryPointCount}`,...e.encryptionKeyArn!==void 0?[` \u2022 Retained KMS key (also orphaned): ${e.encryptionKeyArn}`]:[]," The vault and its KMS key are RemovalPolicy.RETAIN \u2014 destroy cannot delete them."].join(`
2
- `)}export{o as BACKUP_VAULT_NAME,v as accountHasDisasterRecovery,N as describeBackupVaultExists,w as describeSurvivingBackupVault,x as formatSurvivingVaultWarning,B as updateBackupGlobalSettings};
1
+ var k=Object.defineProperty;var i=(e,t)=>k(e,"name",{value:t,configurable:!0});import{DescribeBackupVaultCommand as m,ListBackupVaultsCommand as f,UpdateGlobalSettingsCommand as b}from"@aws-sdk/client-backup";import{success as c,failure as u}from"@fjall/generator";import{BACKUP_VAULT_NAME as o,getErrorMessage as s,maskSensitiveOutput as p}from"@fjall/util";import{composeSdkAbortSignal as l,isAborted as g}from"./types.js";const y={enableCrossAccountBackup:!0,enableDelegatedAdministrator:!0,enableMpa:!1};async function w(e,t,r){try{const n={...y,...t},a={isCrossAccountBackupEnabled:n.enableCrossAccountBackup.toString(),isDelegatedAdministratorEnabled:n.enableDelegatedAdministrator.toString(),isMpaEnabled:n.enableMpa.toString()};return await e.send(new b({GlobalSettings:a}),{abortSignal:l(r)}),c(void 0)}catch(n){return u(new Error(`Failed to update backup global settings: ${s(n)}`))}}i(w,"updateBackupGlobalSettings");function x(e,t){return t===void 0||t===""?!1:e==="production"||e==="compliance"}i(x,"accountHasDisasterRecovery");async function V(e,t){try{return await e.send(new m({BackupVaultName:o}),{abortSignal:l(t)}),c(!0)}catch(r){return r instanceof Error&&r.name==="ResourceNotFoundException"?c(!1):r instanceof Error&&r.name==="AccessDeniedException"?E(e,t):u(new Error(`Failed to describe backup vault "${o}": ${p(s(r))}`))}}i(V,"describeBackupVaultExists");async function E(e,t){try{let r;do{if(g(t))return u(new Error("Backup vault probe aborted"));const n=await e.send(new f({NextToken:r}),{abortSignal:l(t)});if((n.BackupVaultList??[]).some(d=>d.BackupVaultName===o))return c(!0);r=n.NextToken}while(r!==void 0);return c(!1)}catch(r){return u(new Error(`Failed to list backup vaults probing "${o}": ${p(s(r))}`))}}i(E,"listedBackupVaultExists");async function D(e,t,r){try{const n=await e.send(new m({BackupVaultName:o}),{abortSignal:l(r)}),a=n.LockDate,d=a!==void 0&&a.getTime()<=Date.now();return c({vaultName:n.BackupVaultName??o,region:t,locked:n.Locked??!1,...a!==void 0&&{lockDate:a},lockPermanent:d,recoveryPointCount:n.NumberOfRecoveryPoints??0,...n.EncryptionKeyArn!==void 0&&{encryptionKeyArn:n.EncryptionKeyArn}})}catch(n){return n instanceof Error&&n.name==="ResourceNotFoundException"?c(null):u(new Error(`Failed to describe surviving backup vault "${o}": ${p(s(n))}`))}}i(D,"describeSurvivingBackupVault");function $(e){const t=e.locked?e.lockPermanent?"compliance-locked, PERMANENT \u2014 cannot be removed by anyone, including AWS or the root user":e.lockDate!==void 0?`compliance-locked, immutable from ${e.lockDate.toISOString().slice(0,10)}`:"governance-locked \u2014 removable by privileged IAM":"unlocked";return[`Backup vault "${e.vaultName}" in ${e.region} SURVIVES this destroy.`,` \u2022 Lock: ${t}`,` \u2022 Recovery points retained: ${e.recoveryPointCount}`,...e.encryptionKeyArn!==void 0?[` \u2022 Retained KMS key (also orphaned): ${e.encryptionKeyArn}`]:[]," The vault and its KMS key are RemovalPolicy.RETAIN \u2014 destroy cannot delete them."].join(`
2
+ `)}i($,"formatSurvivingVaultWarning");export{o as BACKUP_VAULT_NAME,x as accountHasDisasterRecovery,V as describeBackupVaultExists,D as describeSurvivingBackupVault,$ as formatSurvivingVaultWarning,w as updateBackupGlobalSettings};
@@ -1 +1 @@
1
- import{UpdateCostAllocationTagsStatusCommand as s}from"@aws-sdk/client-cost-explorer";import{success as a,failure as i}from"@fjall/generator";import{getErrorMessage as c}from"@fjall/util";import{composeSdkAbortSignal as l}from"./types.js";async function f(r,o,e){if(o.length===0)return a(void 0);try{const t=o.map(n=>({TagKey:n.TagKey,Status:"Active"}));return await r.send(new s({CostAllocationTagsStatus:t}),{abortSignal:l(e)}),a(void 0)}catch(t){return i(new Error(`Failed to activate cost allocation tags: ${c(t)}`))}}export{f as activateCostAllocationTags};
1
+ var i=Object.defineProperty;var r=(o,t)=>i(o,"name",{value:t,configurable:!0});import{UpdateCostAllocationTagsStatusCommand as c}from"@aws-sdk/client-cost-explorer";import{success as e,failure as l}from"@fjall/generator";import{getErrorMessage as m}from"@fjall/util";import{composeSdkAbortSignal as u}from"./types.js";async function A(o,t,n){if(t.length===0)return e(void 0);try{const a=t.map(s=>({TagKey:s.TagKey,Status:"Active"}));return await o.send(new c({CostAllocationTagsStatus:a}),{abortSignal:u(n)}),e(void 0)}catch(a){return l(new Error(`Failed to activate cost allocation tags: ${m(a)}`))}}r(A,"activateCostAllocationTags");export{A as activateCostAllocationTags};
@@ -1,3 +1,3 @@
1
- import{RegisterDelegatedAdministratorCommand as u}from"@aws-sdk/client-organizations";import{success as f,failure as n}from"@fjall/generator";import{extractErrorName as E,composeSdkAbortSignal as l,isAborted as p,AWS_ERROR_NAMES as s}from"./types.js";import{getErrorMessage as A}from"@fjall/util";const c=["guardduty.amazonaws.com","securityhub.amazonaws.com","config.amazonaws.com","inspector2.amazonaws.com","access-analyzer.amazonaws.com"];async function h(m,t,d=c,i){const e=[];for(const o of d){if(p(i)){const r=e.length>0?`${e.join("; ")}; `:"";return n(new Error(`${r}Aborted: security-delegate registration cancelled by shutdown signal`))}try{await m.send(new u({AccountId:t,ServicePrincipal:o}),{abortSignal:l(i)})}catch(r){const a=E(r);if(a===s.ACCOUNT_ALREADY_REGISTERED)continue;if(a===s.ACCESS_DENIED){const g=e.length>0?`${e.join("; ")}; `:"";return n(new Error(`${g}Access denied when registering delegated admin for ${o}. Ensure your credentials have organizations:RegisterDelegatedAdministrator permission.`))}e.push(`${o}: ${A(r)}`)}}return e.length>0?n(new Error(`Failed to register security delegates for account ${t}:
1
+ var f=Object.defineProperty;var c=(n,r)=>f(n,"name",{value:r,configurable:!0});import{RegisterDelegatedAdministratorCommand as E}from"@aws-sdk/client-organizations";import{success as l,failure as i}from"@fjall/generator";import{extractErrorName as p,composeSdkAbortSignal as A,isAborted as w,AWS_ERROR_NAMES as m}from"./types.js";import{getErrorMessage as S}from"@fjall/util";const d=["guardduty.amazonaws.com","securityhub.amazonaws.com","config.amazonaws.com","inspector2.amazonaws.com","access-analyzer.amazonaws.com"];async function z(n,r,g=d,a){const e=[];for(const t of g){if(w(a)){const o=e.length>0?`${e.join("; ")}; `:"";return i(new Error(`${o}Aborted: security-delegate registration cancelled by shutdown signal`))}try{await n.send(new E({AccountId:r,ServicePrincipal:t}),{abortSignal:A(a)})}catch(o){const s=p(o);if(s===m.ACCOUNT_ALREADY_REGISTERED)continue;if(s===m.ACCESS_DENIED){const u=e.length>0?`${e.join("; ")}; `:"";return i(new Error(`${u}Access denied when registering delegated admin for ${t}. Ensure your credentials have organizations:RegisterDelegatedAdministrator permission.`))}e.push(`${t}: ${S(o)}`)}}return e.length>0?i(new Error(`Failed to register security delegates for account ${r}:
2
2
  ${e.join(`
3
- `)}`)):f(void 0)}export{c as SECURITY_SERVICE_PRINCIPALS,h as registerSecurityDelegates};
3
+ `)}`)):l(void 0)}c(z,"registerSecurityDelegates");export{d as SECURITY_SERVICE_PRINCIPALS,z as registerSecurityDelegates};
@@ -1 +1 @@
1
- import{ListInstancesCommand as s}from"@aws-sdk/client-sso-admin";import{success as o,failure as a}from"@fjall/generator";import{getErrorMessage as c}from"@fjall/util";import{composeSdkAbortSignal as i}from"./types.js";async function l(n,r){try{const e=(await n.send(new s({}),{abortSignal:i(r)})).Instances??[];return o({enabled:e.length>0,instanceCount:e.length})}catch(t){return a(new Error(`Failed to check Identity Centre status: ${c(t)}`))}}export{l as checkIdentityCentreStatus};
1
+ var o=Object.defineProperty;var s=(t,e)=>o(t,"name",{value:e,configurable:!0});import{ListInstancesCommand as a}from"@aws-sdk/client-sso-admin";import{success as c,failure as i}from"@fjall/generator";import{getErrorMessage as m}from"@fjall/util";import{composeSdkAbortSignal as p}from"./types.js";async function h(t,e){try{const r=(await t.send(new a({}),{abortSignal:p(e)})).Instances??[];return c({enabled:r.length>0,instanceCount:r.length})}catch(n){return i(new Error(`Failed to check Identity Centre status: ${m(n)}`))}}s(h,"checkIdentityCentreStatus");export{h as checkIdentityCentreStatus};
@@ -1 +1 @@
1
- import{ListGroupMembershipsCommand as N,ListGroupsCommand as M,ListUsersCommand as w}from"@aws-sdk/client-identitystore";import{ListInstancesCommand as x}from"@aws-sdk/client-sso-admin";import{success as g,failure as o}from"@fjall/generator";import{getErrorMessage as T,maskSensitiveOutput as U}from"@fjall/util";import{composeSdkAbortSignal as u,isAborted as h}from"./types.js";import{IDENTITY_STORE_PAGE_SIZE as p,drainPages as f}from"./pagination.js";function m(c,s){return new Error(`Failed to ${c}: ${U(T(s))}`)}async function L(c,s,n={}){let y,a;try{const r=((await c.send(new x({}),{abortSignal:u(n.abortSignal)})).Instances??[])[0];if(r?.InstanceArn===void 0||r.IdentityStoreId===void 0)return g({status:"no-instance"});y=r.InstanceArn,a=r.IdentityStoreId}catch(t){return o(m("list Identity Centre instances",t))}let l;try{l=await f(async t=>{const r=await s.send(new w({IdentityStoreId:a,MaxResults:p,...t!==void 0&&{NextToken:t}}),{abortSignal:u(n.abortSignal)});return{items:(r.Users??[]).flatMap(e=>e.UserId===void 0?[]:[{userId:e.UserId,...e.UserName!==void 0&&{userName:e.UserName},...e.Emails?.[0]?.Value!==void 0&&{email:e.Emails[0].Value},...e.DisplayName!==void 0&&{displayName:e.DisplayName}}]),nextToken:r.NextToken}},n)}catch(t){return o(m("list identity store users",t))}let I;try{I=await f(async t=>{const r=await s.send(new M({IdentityStoreId:a,MaxResults:p,...t!==void 0&&{NextToken:t}}),{abortSignal:u(n.abortSignal)});return{items:(r.Groups??[]).flatMap(e=>e.GroupId===void 0?[]:[{groupId:e.GroupId,...e.DisplayName!==void 0&&{displayName:e.DisplayName},...e.Description!==void 0&&{description:e.Description}}]),nextToken:r.NextToken}},n)}catch(t){return o(m("list identity store groups",t))}const S=[];let i=!1;for(const t of I.items){if(h(n.abortSignal)){i=!0;break}let r;try{r=await f(async e=>{const b=await s.send(new N({IdentityStoreId:a,GroupId:t.groupId,MaxResults:p,...e!==void 0&&{NextToken:e}}),{abortSignal:u(n.abortSignal)});return{items:(b.GroupMemberships??[]).flatMap(d=>d.MemberId!==void 0&&"UserId"in d.MemberId&&d.MemberId.UserId!==void 0?[d.MemberId.UserId]:[]),nextToken:b.NextToken}},n)}catch(e){return o(m(`list memberships for group "${t.displayName??t.groupId}"`,e))}i=i||r.truncated,S.push({...t,memberUserIds:r.items})}return g({status:"captured",snapshot:{instanceArn:y,identityStoreId:a,users:l.items,groups:S,truncated:l.truncated||I.truncated||i}})}export{L as snapshotIdentityCentre};
1
+ var M=Object.defineProperty;var p=(a,s)=>M(a,"name",{value:s,configurable:!0});import{ListGroupMembershipsCommand as w,ListGroupsCommand as x,ListUsersCommand as T}from"@aws-sdk/client-identitystore";import{ListInstancesCommand as U}from"@aws-sdk/client-sso-admin";import{success as N,failure as u}from"@fjall/generator";import{getErrorMessage as h,maskSensitiveOutput as k}from"@fjall/util";import{composeSdkAbortSignal as m,isAborted as E}from"./types.js";import{IDENTITY_STORE_PAGE_SIZE as f,drainPages as y}from"./pagination.js";function c(a,s){return new Error(`Failed to ${a}: ${k(h(s))}`)}p(c,"snapshotError");async function _(a,s,n={}){let S,i;try{const r=((await a.send(new U({}),{abortSignal:m(n.abortSignal)})).Instances??[])[0];if(r?.InstanceArn===void 0||r.IdentityStoreId===void 0)return N({status:"no-instance"});S=r.InstanceArn,i=r.IdentityStoreId}catch(t){return u(c("list Identity Centre instances",t))}let l;try{l=await y(async t=>{const r=await s.send(new T({IdentityStoreId:i,MaxResults:f,...t!==void 0&&{NextToken:t}}),{abortSignal:m(n.abortSignal)});return{items:(r.Users??[]).flatMap(e=>e.UserId===void 0?[]:[{userId:e.UserId,...e.UserName!==void 0&&{userName:e.UserName},...e.Emails?.[0]?.Value!==void 0&&{email:e.Emails[0].Value},...e.DisplayName!==void 0&&{displayName:e.DisplayName}}]),nextToken:r.NextToken}},n)}catch(t){return u(c("list identity store users",t))}let I;try{I=await y(async t=>{const r=await s.send(new x({IdentityStoreId:i,MaxResults:f,...t!==void 0&&{NextToken:t}}),{abortSignal:m(n.abortSignal)});return{items:(r.Groups??[]).flatMap(e=>e.GroupId===void 0?[]:[{groupId:e.GroupId,...e.DisplayName!==void 0&&{displayName:e.DisplayName},...e.Description!==void 0&&{description:e.Description}}]),nextToken:r.NextToken}},n)}catch(t){return u(c("list identity store groups",t))}const b=[];let d=!1;for(const t of I.items){if(E(n.abortSignal)){d=!0;break}let r;try{r=await y(async e=>{const g=await s.send(new w({IdentityStoreId:i,GroupId:t.groupId,MaxResults:f,...e!==void 0&&{NextToken:e}}),{abortSignal:m(n.abortSignal)});return{items:(g.GroupMemberships??[]).flatMap(o=>o.MemberId!==void 0&&"UserId"in o.MemberId&&o.MemberId.UserId!==void 0?[o.MemberId.UserId]:[]),nextToken:g.NextToken}},n)}catch(e){return u(c(`list memberships for group "${t.displayName??t.groupId}"`,e))}d=d||r.truncated,b.push({...t,memberUserIds:r.items})}return N({status:"captured",snapshot:{instanceArn:S,identityStoreId:i,users:l.items,groups:b,truncated:l.truncated||I.truncated||d}})}p(_,"snapshotIdentityCentre");export{_ as snapshotIdentityCentre};
@@ -1 +1 @@
1
- function i(n,t){return n.filter(e=>e.JoinedMethod==="INVITED"&&e.Id!==t&&(e.State??e.Status)==="ACTIVE")}const a=5;function l(n){const t=n.slice(0,a).map(o=>`${o.Name??"unnamed"} (${o.Id??"unknown id"})`).join(", "),e=n.length-a,r=e>0?`${t} and ${e} more`:t,s=n.length===1?"account":"accounts";return`Centralised root access now applies to ${n.length} imported member ${s} that joined this organisation by invitation and previously held self-managed root credentials: ${r}. Root credentials for these accounts are now centrally managed.`}export{l as formatImportedAccountsWarning,i as selectImportedMemberAccounts};
1
+ var l=Object.defineProperty;var o=(e,t)=>l(e,"name",{value:t,configurable:!0});function d(e,t){return e.filter(n=>n.JoinedMethod==="INVITED"&&n.Id!==t&&(n.State??n.Status)==="ACTIVE")}o(d,"selectImportedMemberAccounts");const r=5;function m(e){const t=e.slice(0,r).map(a=>`${a.Name??"unnamed"} (${a.Id??"unknown id"})`).join(", "),n=e.length-r,s=n>0?`${t} and ${n} more`:t,i=e.length===1?"account":"accounts";return`Centralised root access now applies to ${e.length} imported member ${i} that joined this organisation by invitation and previously held self-managed root credentials: ${s}. Root credentials for these accounts are now centrally managed.`}o(m,"formatImportedAccountsWarning");export{m as formatImportedAccountsWarning,d as selectImportedMemberAccounts};
@@ -1 +1 @@
1
- import{EnableIpamOrganizationAdminAccountCommand as a}from"@aws-sdk/client-ec2";import{success as t,failure as i}from"@fjall/generator";import{getErrorMessage as m}from"@fjall/util";import{composeSdkAbortSignal as c}from"./types.js";async function u(e,r,n){try{return await e.send(new a({DryRun:!1,DelegatedAdminAccountId:r}),{abortSignal:c(n)}),t(void 0)}catch(o){return i(new Error(`Failed to enable IPAM delegation for account ${r}: ${m(o)}`))}}export{u as enableIpamDelegatedAdmin};
1
+ var t=Object.defineProperty;var n=(e,r)=>t(e,"name",{value:r,configurable:!0});import{EnableIpamOrganizationAdminAccountCommand as i}from"@aws-sdk/client-ec2";import{success as m,failure as c}from"@fjall/generator";import{getErrorMessage as l}from"@fjall/util";import{composeSdkAbortSignal as d}from"./types.js";async function b(e,r,o){try{return await e.send(new i({DryRun:!1,DelegatedAdminAccountId:r}),{abortSignal:d(o)}),m(void 0)}catch(a){return c(new Error(`Failed to enable IPAM delegation for account ${r}: ${l(a)}`))}}n(b,"enableIpamDelegatedAdmin");export{b as enableIpamDelegatedAdmin};
@@ -1 +1 @@
1
- import{ListOpenIDConnectProvidersCommand as l,ListRolesCommand as D,CreateOpenIDConnectProviderCommand as O,DeleteOpenIDConnectProviderCommand as w}from"@aws-sdk/client-iam";import{FJALL_OIDC_AUDIENCE as y,FJALL_OIDC_ISSUER_DOMAIN as f,FJALL_OIDC_PLACEHOLDER_THUMBPRINT as A,success as d,failure as v}from"@fjall/generator";import{maskSensitiveOutput as m,getErrorMessage as I}from"@fjall/util";import{logger as s}from"@fjall/util/logger";import{composeSdkAbortSignal as a,isAborted as c}from"./types.js";const C=`:oidc-provider/${f}`;async function g(i,e){try{if(c(e))return d({deleted:!1,reason:"aborted"});const r=((await i.send(new l({}),{abortSignal:a(e)})).OpenIDConnectProviderList??[]).map(o=>o.Arn).find(o=>typeof o=="string"&&o.endsWith(C));if(r===void 0)return d({deleted:!1,reason:"provider not present"});let t;do{if(c(e))return d({deleted:!1,reason:"aborted"});const o=await i.send(new D({Marker:t}),{abortSignal:a(e)});for(const p of o.Roles??[]){const u=p.AssumeRolePolicyDocument;if(typeof u=="string"&&decodeURIComponent(u).includes(r))return d({deleted:!1,reason:`still referenced by role ${p.RoleName??"(unknown)"}`})}t=o.IsTruncated===!0?o.Marker:void 0}while(t!==void 0);return await i.send(new w({OpenIDConnectProviderArn:r}),{abortSignal:a(e)}),d({deleted:!0,reason:"deleted orphaned provider"})}catch(n){return c(e)?d({deleted:!1,reason:"aborted"}):n instanceof Error&&n.name==="NoSuchEntityException"?d({deleted:!1,reason:"provider not present"}):v(new Error(`Failed to evaluate OIDC provider teardown: ${I(n)}`))}}async function h(i,e){try{if(c(e))return d({status:"aborted"});const r=((await i.send(new l({}),{abortSignal:a(e)})).OpenIDConnectProviderList??[]).map(o=>o.Arn).find(o=>typeof o=="string"&&o.endsWith(C));if(r!==void 0)return d({status:"present",providerArn:r});const t=await i.send(new O({Url:`https://${f}`,ClientIDList:[y],ThumbprintList:[A]}),{abortSignal:a(e)});return d({status:"recreated",...t.OpenIDConnectProviderArn!==void 0&&{providerArn:t.OpenIDConnectProviderArn}})}catch(n){return c(e)?d({status:"aborted"}):n instanceof Error&&n.name==="EntityAlreadyExistsException"?d({status:"present"}):v(new Error(`Failed to ensure OIDC provider presence: ${I(n)}`))}}async function F(i,e,n){const r=await h(i,n);if(!r.success){const t=m(r.error.message);s.debug("oidcReconcile","OIDC provider ensure probe failed",{error:t}),e.onLog?.(`Could not verify the Fjall OIDC provider after deploy: ${t}. If scans or deploys later fail with InvalidIdentityToken, re-run this deploy or reconnect the account.`,"warn");return}if(r.data.status==="recreated"){s.info("oidcReconcile","Recreated missing account-global OIDC provider",{providerArn:r.data.providerArn}),e.onLog?.("The Fjall OIDC provider was missing after the stack deploy and has been restored","warn");return}s.debug("oidcReconcile","OIDC provider verified after deploy",{status:r.data.status})}async function _(i,e,n){const r=await g(i,n);if(!r.success){const t=m(r.error.message);s.debug("oidcTeardown","OIDC provider teardown probe failed",{error:t}),e.onLog?.(`OIDC provider cleanup skipped: ${t}`,"warn");return}if(r.data.deleted){s.info("oidcTeardown","Deleted orphaned account-global OIDC provider"),e.onLog?.("Removed the orphaned Fjall OIDC provider","info");return}s.debug("oidcTeardown","OIDC provider retained",{reason:r.data.reason})}export{_ as cleanupOrphanedOidcProvider,g as deleteOidcProviderIfUnreferenced,h as ensureOidcProviderPresent,F as reconcileOidcProviderAfterDeploy};
1
+ var O=Object.defineProperty;var a=(t,e)=>O(t,"name",{value:e,configurable:!0});import{ListOpenIDConnectProvidersCommand as f,ListRolesCommand as w,CreateOpenIDConnectProviderCommand as y,DeleteOpenIDConnectProviderCommand as A}from"@aws-sdk/client-iam";import{FJALL_OIDC_AUDIENCE as g,FJALL_OIDC_ISSUER_DOMAIN as v,FJALL_OIDC_PLACEHOLDER_THUMBPRINT as h,success as i,failure as m}from"@fjall/generator";import{maskSensitiveOutput as I,getErrorMessage as C}from"@fjall/util";import{logger as s}from"@fjall/util/logger";import{composeSdkAbortSignal as c,isAborted as p}from"./types.js";const D=`:oidc-provider/${v}`;async function L(t,e){try{if(p(e))return i({deleted:!1,reason:"aborted"});const r=((await t.send(new f({}),{abortSignal:c(e)})).OpenIDConnectProviderList??[]).map(o=>o.Arn).find(o=>typeof o=="string"&&o.endsWith(D));if(r===void 0)return i({deleted:!1,reason:"provider not present"});let d;do{if(p(e))return i({deleted:!1,reason:"aborted"});const o=await t.send(new w({Marker:d}),{abortSignal:c(e)});for(const u of o.Roles??[]){const l=u.AssumeRolePolicyDocument;if(typeof l=="string"&&decodeURIComponent(l).includes(r))return i({deleted:!1,reason:`still referenced by role ${u.RoleName??"(unknown)"}`})}d=o.IsTruncated===!0?o.Marker:void 0}while(d!==void 0);return await t.send(new A({OpenIDConnectProviderArn:r}),{abortSignal:c(e)}),i({deleted:!0,reason:"deleted orphaned provider"})}catch(n){return p(e)?i({deleted:!1,reason:"aborted"}):n instanceof Error&&n.name==="NoSuchEntityException"?i({deleted:!1,reason:"provider not present"}):m(new Error(`Failed to evaluate OIDC provider teardown: ${C(n)}`))}}a(L,"deleteOidcProviderIfUnreferenced");async function P(t,e){try{if(p(e))return i({status:"aborted"});const r=((await t.send(new f({}),{abortSignal:c(e)})).OpenIDConnectProviderList??[]).map(o=>o.Arn).find(o=>typeof o=="string"&&o.endsWith(D));if(r!==void 0)return i({status:"present",providerArn:r});const d=await t.send(new y({Url:`https://${v}`,ClientIDList:[g],ThumbprintList:[h]}),{abortSignal:c(e)});return i({status:"recreated",...d.OpenIDConnectProviderArn!==void 0&&{providerArn:d.OpenIDConnectProviderArn}})}catch(n){return p(e)?i({status:"aborted"}):n instanceof Error&&n.name==="EntityAlreadyExistsException"?i({status:"present"}):m(new Error(`Failed to ensure OIDC provider presence: ${C(n)}`))}}a(P,"ensureOidcProviderPresent");async function T(t,e,n){const r=await P(t,n);if(!r.success){const d=I(r.error.message);s.debug("oidcReconcile","OIDC provider ensure probe failed",{error:d}),e.onLog?.(`Could not verify the Fjall OIDC provider after deploy: ${d}. If scans or deploys later fail with InvalidIdentityToken, re-run this deploy or reconnect the account.`,"warn");return}if(r.data.status==="recreated"){s.info("oidcReconcile","Recreated missing account-global OIDC provider",{providerArn:r.data.providerArn}),e.onLog?.("The Fjall OIDC provider was missing after the stack deploy and has been restored","warn");return}s.debug("oidcReconcile","OIDC provider verified after deploy",{status:r.data.status})}a(T,"reconcileOidcProviderAfterDeploy");async function x(t,e,n){const r=await L(t,n);if(!r.success){const d=I(r.error.message);s.debug("oidcTeardown","OIDC provider teardown probe failed",{error:d}),e.onLog?.(`OIDC provider cleanup skipped: ${d}`,"warn");return}if(r.data.deleted){s.info("oidcTeardown","Deleted orphaned account-global OIDC provider"),e.onLog?.("Removed the orphaned Fjall OIDC provider","info");return}s.debug("oidcTeardown","OIDC provider retained",{reason:r.data.reason})}a(x,"cleanupOrphanedOidcProvider");export{x as cleanupOrphanedOidcProvider,L as deleteOidcProviderIfUnreferenced,P as ensureOidcProviderPresent,T as reconcileOidcProviderAfterDeploy};
@@ -1 +1 @@
1
- import{DescribeOrganizationCommand as l,CreateOrganizationCommand as m,ListRootsCommand as w}from"@aws-sdk/client-organizations";import{success as s,failure as a}from"@fjall/generator";import{getErrorMessage as d}from"@fjall/util";import{extractErrorName as i,isAccessDenied as g,composeSdkAbortSignal as f,AWS_ERROR_NAMES as A}from"./types.js";async function M(n,o){try{let e=!1,r=await c(n,o);if(!r)try{r=(await n.send(new m({FeatureSet:"ALL"}),{abortSignal:f(o)})).Organization??null,e=!0}catch(u){if(i(u)==="AlreadyInOrganizationException"){if(r=await c(n,o),!r)return a(new Error("Account is already in an organisation but DescribeOrganization returned nothing"))}else throw u}if(!r?.Id||!r.MasterAccountId)return a(new Error("Organisation is missing required fields (Id or MasterAccountId)"));const t=await I(n,o);return t.success?s({orgId:r.Id,rootId:t.data,managementAccountId:r.MasterAccountId,created:e}):t}catch(e){return a(new Error(`Failed to ensure organisation exists: ${d(e)}`))}}async function h(n,o,e){try{const r=await c(n,o,e);if(!r)return s(null);if(!r.Id||!r.MasterAccountId)return a(new Error("Organisation is missing required fields (Id or MasterAccountId)"));const t=await I(n,o);return t.success?s({orgId:r.Id,rootId:t.data,managementAccountId:r.MasterAccountId,created:!1}):t}catch(r){return e?.tolerateAccessDenied===!0&&g(i(r))?s(null):a(new Error(`Failed to describe organisation: ${d(r)}`))}}async function b(n,o){try{const e=await c(n,o);if(!e)return s(null);if(!e.Id||!e.MasterAccountId)return a(new Error("Organisation is missing required fields (Id or MasterAccountId)"));let r;try{const t=await I(n,o);t.success&&(r=t.data)}catch(t){if(!g(i(t)))throw t}return s({orgId:e.Id,managementAccountId:e.MasterAccountId,...r!==void 0?{rootId:r}:{}})}catch(e){return a(new Error(`Failed to describe organisation: ${d(e)}`))}}async function I(n,o){const r=(await n.send(new w({}),{abortSignal:f(o)})).Roots??[];return r.length===0||!r[0]?.Id?a(new Error("No organisation root found")):s(r[0].Id)}async function c(n,o,e){try{return(await n.send(new l({}),{abortSignal:f(o)})).Organization??null}catch(r){const t=i(r);if(t===A.ORGS_NOT_IN_USE||e?.tolerateAccessDenied===!0&&g(t))return null;throw r}}export{h as describeOrganisation,b as describeOrganisationAsMember,M as ensureOrganisationExists};
1
+ var m=Object.defineProperty;var i=(t,n)=>m(t,"name",{value:n,configurable:!0});import{DescribeOrganizationCommand as w,CreateOrganizationCommand as A,ListRootsCommand as E}from"@aws-sdk/client-organizations";import{success as s,failure as a}from"@fjall/generator";import{getErrorMessage as g}from"@fjall/util";import{extractErrorName as c,isAccessDenied as f,composeSdkAbortSignal as I,AWS_ERROR_NAMES as O}from"./types.js";async function N(t,n){try{let e=!1,r=await u(t,n);if(!r)try{r=(await t.send(new A({FeatureSet:"ALL"}),{abortSignal:I(n)})).Organization??null,e=!0}catch(d){if(c(d)==="AlreadyInOrganizationException"){if(r=await u(t,n),!r)return a(new Error("Account is already in an organisation but DescribeOrganization returned nothing"))}else throw d}if(!r?.Id||!r.MasterAccountId)return a(new Error("Organisation is missing required fields (Id or MasterAccountId)"));const o=await l(t,n);return o.success?s({orgId:r.Id,rootId:o.data,managementAccountId:r.MasterAccountId,created:e}):o}catch(e){return a(new Error(`Failed to ensure organisation exists: ${g(e)}`))}}i(N,"ensureOrganisationExists");async function x(t,n,e){try{const r=await u(t,n,e);if(!r)return s(null);if(!r.Id||!r.MasterAccountId)return a(new Error("Organisation is missing required fields (Id or MasterAccountId)"));const o=await l(t,n);return o.success?s({orgId:r.Id,rootId:o.data,managementAccountId:r.MasterAccountId,created:!1}):o}catch(r){return e?.tolerateAccessDenied===!0&&f(c(r))?s(null):a(new Error(`Failed to describe organisation: ${g(r)}`))}}i(x,"describeOrganisation");async function z(t,n){try{const e=await u(t,n);if(!e)return s(null);if(!e.Id||!e.MasterAccountId)return a(new Error("Organisation is missing required fields (Id or MasterAccountId)"));let r;try{const o=await l(t,n);o.success&&(r=o.data)}catch(o){if(!f(c(o)))throw o}return s({orgId:e.Id,managementAccountId:e.MasterAccountId,...r!==void 0?{rootId:r}:{}})}catch(e){return a(new Error(`Failed to describe organisation: ${g(e)}`))}}i(z,"describeOrganisationAsMember");async function l(t,n){const r=(await t.send(new E({}),{abortSignal:I(n)})).Roots??[];return r.length===0||!r[0]?.Id?a(new Error("No organisation root found")):s(r[0].Id)}i(l,"getOrganisationRootId");async function u(t,n,e){try{return(await t.send(new w({}),{abortSignal:I(n)})).Organization??null}catch(r){const o=c(r);if(o===O.ORGS_NOT_IN_USE||e?.tolerateAccessDenied===!0&&f(o))return null;throw r}}i(u,"describeOrganisationRaw");export{x as describeOrganisation,z as describeOrganisationAsMember,N as ensureOrganisationExists};
@@ -1 +1 @@
1
- import{CreateOrganizationalUnitCommand as x,ListOrganizationalUnitsForParentCommand as C,ListParentsCommand as b,MoveAccountCommand as _}from"@aws-sdk/client-organizations";import{success as f,failure as u}from"@fjall/generator";import{extractErrorName as N,isOULeaf as A,composeSdkAbortSignal as m,isAborted as p,AWS_ERROR_NAMES as E}from"./types.js";import{accountTier as v,getErrorMessage as y}from"@fjall/util";async function U(o,s,t){const e=await o.send(new C({ParentId:s}),{abortSignal:m(t)});let n=e.OrganizationalUnits??[],a=e.NextToken;for(;a;){if(p(t))throw new Error("Aborted: OU listing cancelled by shutdown signal");const r=await o.send(new C({ParentId:s,NextToken:a}),{abortSignal:m(t)});n=n.concat(r.OrganizationalUnits??[]),a=r.NextToken}return n}async function F(o,s,t){try{return(await o.send(new b({ChildId:s}),{abortSignal:m(t)})).Parents?.[0]?.Id}catch(e){if(N(e)===E.CHILD_NOT_FOUND)return;throw e}}async function L(o,s,t,e,n){const a=e.find(r=>r.Name===t);if(a?.Id)return f(a.Id);try{const c=(await o.send(new x({Name:t,ParentId:s}),{abortSignal:m(n)})).OrganizationalUnit;return c?.Id?f(c.Id):u(new Error(`OU "${t}" was created but has no ID`))}catch(r){return u(new Error(`Failed to create OU "${t}": ${y(r)}`))}}function P(o){return o.charAt(0).toUpperCase()+o.slice(1)}async function D(o,s,t,e){const n={};if(t.length===0)return f(n);const a=await U(o,s,e);for(const r of t){const c=P(r),i=await L(o,s,c,a,e);if(!i.success)return u(i.error);n[r.toLowerCase()]=i.data,a.push({Id:i.data,Name:c})}return f(n)}async function $(o,s,t,e,n,a,r){const c=await U(o,s,r);for(const[i,d]of Object.entries(t)){const l=P(i),h=await L(o,s,l,c,r);if(!h.success)return u(h.error);const O=h.data,g=n?`${n}.${i.toLowerCase()}`:i.toLowerCase();if(e[g]=O,n){const w=i.toLowerCase();a.has(w)||(e[w]=O)}if(c.push({Id:O,Name:l}),!A(d)){const w=await $(o,O,d,e,g,a,r);if(!w.success)return w}}return f(void 0)}async function H(o,s,t,e){if(p(e))return u(new Error("Aborted: OU reconciliation cancelled by shutdown signal"));try{if(Array.isArray(t))return await D(o,s,t,e);const n={},a=new Set(Object.keys(t).map(c=>c.toLowerCase())),r=await $(o,s,t,n,"",a,e);return r.success?f(n):u(r.error)}catch(n){return u(new Error(`Failed to ensure OUs exist: ${y(n)}`))}}function T(o,s,t=""){const e={};for(const[n,a]of Object.entries(o)){const r=t?`${t}.${n.toLowerCase()}`:n.toLowerCase(),c=s[r];if(A(a)){if(c)for(const i of a)e[i.toLowerCase()]=c}else Object.assign(e,T(a,s,r))}return e}async function S(o,s,t,e,n){try{if(t.length===0)return f({moved:0,alreadyPlaced:0});let a=0,r=0;for(const c of t){if(p(n))return u(new Error("Aborted: account placement cancelled by shutdown signal"));if(v(c)==="organisation")continue;const i=e?e[c.name.toLowerCase()]:s[c.environment.toLowerCase()];if(!i)continue;const d=await F(o,c.id,n);if(d){if(d===i){r++;continue}try{await o.send(new _({AccountId:c.id,SourceParentId:d,DestinationParentId:i}),{abortSignal:m(n)}),a++}catch(l){if(N(l)===E.ACCOUNT_NOT_FOUND)continue;throw l}}}return f({moved:a,alreadyPlaced:r})}catch(a){return u(new Error(`Failed to place accounts in OUs: ${y(a)}`))}}const I="development",k=3;async function K(o,s,t){try{const e=[];let n=[s];for(let a=0;a<k;a++){const r=[];for(const c of n){if(p(t))return u(new Error("Aborted: development-OU lookup cancelled by shutdown signal"));const i=await U(o,c,t);for(const d of i)if(d.Id!==void 0){if(d.Name?.toLowerCase()===I){e.push(d.Id);continue}r.push(d.Id)}}if(r.length===0)break;n=r}return e.length===0?f(void 0):e.length>1?u(new Error(`Found ${e.length} organisational units named "${I}" (${e.join(", ")}); cannot safely target the dev-isolation SCP. A flat\u2192nested migration can leave a stray empty "${I}" OU at the root \u2014 remove the duplicate so exactly one remains.`)):f(e[0])}catch(e){return u(new Error(`Failed to locate development OU: ${y(e)}`))}}export{T as buildAccountToOUMap,H as ensureOrganisationalUnitsExist,K as findDevelopmentOuId,S as placeAccountsInOUs};
1
+ var b=Object.defineProperty;var f=(r,s)=>b(r,"name",{value:s,configurable:!0});import{CreateOrganizationalUnitCommand as _,ListOrganizationalUnitsForParentCommand as N,ListParentsCommand as v,MoveAccountCommand as F}from"@aws-sdk/client-organizations";import{success as l,failure as u}from"@fjall/generator";import{extractErrorName as A,isOULeaf as E,composeSdkAbortSignal as h,isAborted as y,AWS_ERROR_NAMES as L}from"./types.js";import{accountTier as D,getErrorMessage as U}from"@fjall/util";async function I(r,s,t){const e=await r.send(new N({ParentId:s}),{abortSignal:h(t)});let n=e.OrganizationalUnits??[],a=e.NextToken;for(;a;){if(y(t))throw new Error("Aborted: OU listing cancelled by shutdown signal");const o=await r.send(new N({ParentId:s,NextToken:a}),{abortSignal:h(t)});n=n.concat(o.OrganizationalUnits??[]),a=o.NextToken}return n}f(I,"listOUsForParent");async function T(r,s,t){try{return(await r.send(new v({ChildId:s}),{abortSignal:h(t)})).Parents?.[0]?.Id}catch(e){if(A(e)===L.CHILD_NOT_FOUND)return;throw e}}f(T,"getParentId");async function P(r,s,t,e,n){const a=e.find(o=>o.Name===t);if(a?.Id)return l(a.Id);try{const c=(await r.send(new _({Name:t,ParentId:s}),{abortSignal:h(n)})).OrganizationalUnit;return c?.Id?l(c.Id):u(new Error(`OU "${t}" was created but has no ID`))}catch(o){return u(new Error(`Failed to create OU "${t}": ${U(o)}`))}}f(P,"findOrCreateOU");function $(r){return r.charAt(0).toUpperCase()+r.slice(1)}f($,"canonicaliseOuName");async function k(r,s,t,e){const n={};if(t.length===0)return l(n);const a=await I(r,s,e);for(const o of t){const c=$(o),i=await P(r,s,c,a,e);if(!i.success)return u(i.error);n[o.toLowerCase()]=i.data,a.push({Id:i.data,Name:c})}return l(n)}f(k,"ensureFlatOUs");async function x(r,s,t,e,n,a,o){const c=await I(r,s,o);for(const[i,d]of Object.entries(t)){const w=$(i),O=await P(r,s,w,c,o);if(!O.success)return u(O.error);const p=O.data,C=n?`${n}.${i.toLowerCase()}`:i.toLowerCase();if(e[C]=p,n){const m=i.toLowerCase();a.has(m)||(e[m]=p)}if(c.push({Id:p,Name:w}),!E(d)){const m=await x(r,p,d,e,C,a,o);if(!m.success)return m}}return l(void 0)}f(x,"ensureNestedOUs");async function V(r,s,t,e){if(y(e))return u(new Error("Aborted: OU reconciliation cancelled by shutdown signal"));try{if(Array.isArray(t))return await k(r,s,t,e);const n={},a=new Set(Object.keys(t).map(c=>c.toLowerCase())),o=await x(r,s,t,n,"",a,e);return o.success?l(n):u(o.error)}catch(n){return u(new Error(`Failed to ensure OUs exist: ${U(n)}`))}}f(V,"ensureOrganisationalUnitsExist");function R(r,s,t=""){const e={};for(const[n,a]of Object.entries(r)){const o=t?`${t}.${n.toLowerCase()}`:n.toLowerCase(),c=s[o];if(E(a)){if(c)for(const i of a)e[i.toLowerCase()]=c}else Object.assign(e,R(a,s,o))}return e}f(R,"buildAccountToOUMap");async function W(r,s,t,e,n){try{if(t.length===0)return l({moved:0,alreadyPlaced:0});let a=0,o=0;for(const c of t){if(y(n))return u(new Error("Aborted: account placement cancelled by shutdown signal"));if(D(c)==="organisation")continue;const i=e?e[c.name.toLowerCase()]:s[c.environment.toLowerCase()];if(!i)continue;const d=await T(r,c.id,n);if(d){if(d===i){o++;continue}try{await r.send(new F({AccountId:c.id,SourceParentId:d,DestinationParentId:i}),{abortSignal:h(n)}),a++}catch(w){if(A(w)===L.ACCOUNT_NOT_FOUND)continue;throw w}}}return l({moved:a,alreadyPlaced:o})}catch(a){return u(new Error(`Failed to place accounts in OUs: ${U(a)}`))}}f(W,"placeAccountsInOUs");const g="development",j=3;async function X(r,s,t){try{const e=[];let n=[s];for(let a=0;a<j;a++){const o=[];for(const c of n){if(y(t))return u(new Error("Aborted: development-OU lookup cancelled by shutdown signal"));const i=await I(r,c,t);for(const d of i)if(d.Id!==void 0){if(d.Name?.toLowerCase()===g){e.push(d.Id);continue}o.push(d.Id)}}if(o.length===0)break;n=o}return e.length===0?l(void 0):e.length>1?u(new Error(`Found ${e.length} organisational units named "${g}" (${e.join(", ")}); cannot safely target the dev-isolation SCP. A flat\u2192nested migration can leave a stray empty "${g}" OU at the root \u2014 remove the duplicate so exactly one remains.`)):l(e[0])}catch(e){return u(new Error(`Failed to locate development OU: ${U(e)}`))}}f(X,"findDevelopmentOuId");export{R as buildAccountToOUMap,V as ensureOrganisationalUnitsExist,X as findDevelopmentOuId,W as placeAccountsInOUs};
@@ -1 +1 @@
1
- import{isAborted as i}from"./types.js";const c=20,d=50;async function x(o,n={}){const s=n.maxPages??c,e=[];let t,a=0;do{if(i(n.abortSignal)||a>=s)return{items:e,truncated:!0};const r=await o(t);e.push(...r.items),t=r.nextToken,a+=1}while(t!==void 0);return{items:e,truncated:!1}}export{c as DEFAULT_MAX_PAGES,d as IDENTITY_STORE_PAGE_SIZE,x as drainPages};
1
+ var c=Object.defineProperty;var s=(t,e)=>c(t,"name",{value:e,configurable:!0});import{isAborted as u}from"./types.js";const d=20,m=50;async function p(t,e={}){const i=e.maxPages??d,n=[];let a,r=0;do{if(u(e.abortSignal)||r>=i)return{items:n,truncated:!0};const o=await t(a);n.push(...o.items),a=o.nextToken,r+=1}while(a!==void 0);return{items:n,truncated:!1}}s(p,"drainPages");export{d as DEFAULT_MAX_PAGES,m as IDENTITY_STORE_PAGE_SIZE,p as drainPages};
@@ -1 +1 @@
1
- import{EnablePolicyTypeCommand as m,ListRootsCommand as w,PolicyType as i}from"@aws-sdk/client-organizations";import{success as T,failure as p}from"@fjall/generator";import{getErrorMessage as P}from"@fjall/util";import{extractErrorName as _,composeSdkAbortSignal as E,isAborted as L}from"./types.js";import{sleepAbortable as S}from"../../util/sleepAbortable.js";const d=[i.SERVICE_CONTROL_POLICY,i.TAG_POLICY,i.BACKUP_POLICY,i.AISERVICES_OPT_OUT_POLICY],u=2e3,I=6e4;async function Y(t,n,e={}){try{for(const r of d){if(L(e.abortSignal))return p(new Error("Aborted: policy-type enablement cancelled by shutdown signal"));try{await t.send(new m({RootId:n,PolicyType:r}),{abortSignal:E(e.abortSignal)})}catch(a){if(_(a)==="PolicyTypeAlreadyEnabledException")continue;throw a}}return await g(t,n,e),T(void 0)}catch(r){return p(new Error(`Failed to enable policy types: ${P(r)}`))}}async function g(t,n,e){const r=e.pollIntervalMs??u,a=e.pollTimeoutMs??I,l=new Set(d),f=Date.now();for(;;){const b=(await t.send(new w({}),{abortSignal:E(e.abortSignal)})).Roots?.find(o=>o.Id===n),s=new Set;for(const o of b?.PolicyTypes??[])o.Status==="ENABLED"&&o.Type&&s.add(o.Type);const c=[...l].filter(o=>!s.has(o));if(c.length===0)return;const y=Date.now()-f;if(y>=a)throw new Error(`Policy types still PENDING_ENABLE after ${y}ms: ${c.join(", ")}`);if(r>0&&(await S(r,e.abortSignal),e.abortSignal?.aborted===!0))throw new Error("Aborted while waiting for policy types to enable")}}export{Y as enablePolicyTypes};
1
+ var w=Object.defineProperty;var l=(r,t)=>w(r,"name",{value:t,configurable:!0});import{EnablePolicyTypeCommand as T,ListRootsCommand as P,PolicyType as i}from"@aws-sdk/client-organizations";import{success as _,failure as E}from"@fjall/generator";import{getErrorMessage as L}from"@fjall/util";import{extractErrorName as S,composeSdkAbortSignal as d,isAborted as u}from"./types.js";import{sleepAbortable as I}from"../../util/sleepAbortable.js";const f=[i.SERVICE_CONTROL_POLICY,i.TAG_POLICY,i.BACKUP_POLICY,i.AISERVICES_OPT_OUT_POLICY],g=2e3,A=6e4;async function D(r,t,e={}){try{for(const n of f){if(u(e.abortSignal))return E(new Error("Aborted: policy-type enablement cancelled by shutdown signal"));try{await r.send(new T({RootId:t,PolicyType:n}),{abortSignal:d(e.abortSignal)})}catch(a){if(S(a)==="PolicyTypeAlreadyEnabledException")continue;throw a}}return await O(r,t,e),_(void 0)}catch(n){return E(new Error(`Failed to enable policy types: ${L(n)}`))}}l(D,"enablePolicyTypes");async function O(r,t,e){const n=e.pollIntervalMs??g,a=e.pollTimeoutMs??A,s=new Set(f),b=Date.now();for(;;){const m=(await r.send(new P({}),{abortSignal:d(e.abortSignal)})).Roots?.find(o=>o.Id===t),c=new Set;for(const o of m?.PolicyTypes??[])o.Status==="ENABLED"&&o.Type&&c.add(o.Type);const y=[...s].filter(o=>!c.has(o));if(y.length===0)return;const p=Date.now()-b;if(p>=a)throw new Error(`Policy types still PENDING_ENABLE after ${p}ms: ${y.join(", ")}`);if(n>0&&(await I(n,e.abortSignal),e.abortSignal?.aborted===!0))throw new Error("Aborted while waiting for policy types to enable")}}l(O,"waitForPolicyTypesEnabled");export{D as enablePolicyTypes};
@@ -1 +1 @@
1
- import{EnableSharingWithAwsOrganizationCommand as a}from"@aws-sdk/client-ram";import{success as o,failure as i}from"@fjall/generator";import{getErrorMessage as t}from"@fjall/util";import{composeSdkAbortSignal as m}from"./types.js";async function l(r,e){try{return await r.send(new a({}),{abortSignal:m(e)}),o(void 0)}catch(n){return i(new Error(`Failed to enable RAM sharing: ${t(n)}`))}}export{l as enableRamSharing};
1
+ var o=Object.defineProperty;var n=(r,e)=>o(r,"name",{value:e,configurable:!0});import{EnableSharingWithAwsOrganizationCommand as i}from"@aws-sdk/client-ram";import{success as t,failure as m}from"@fjall/generator";import{getErrorMessage as s}from"@fjall/util";import{composeSdkAbortSignal as g}from"./types.js";async function u(r,e){try{return await r.send(new i({}),{abortSignal:g(e)}),t(void 0)}catch(a){return m(new Error(`Failed to enable RAM sharing: ${s(a)}`))}}n(u,"enableRamSharing");export{u as enableRamSharing};
@@ -1,3 +1,3 @@
1
- import{ListOrganizationsFeaturesCommand as h,EnableOrganizationsRootCredentialsManagementCommand as S,EnableOrganizationsRootSessionsCommand as w}from"@aws-sdk/client-iam";import{success as C,failure as a}from"@fjall/generator";import{composeSdkAbortSignal as d,extractErrorName as u,isAborted as R,isAccessDenied as E}from"./types.js";import{getErrorMessage as b,maskSensitiveOutput as p}from"@fjall/util";const y=["RootCredentialsManagement","RootSessions"],z={RootCredentialsManagement:{CommandClass:S,permission:"iam:EnableOrganizationsRootCredentialsManagement"},RootSessions:{CommandClass:w,permission:"iam:EnableOrganizationsRootSessions"}};class r extends Error{partialSummary;constructor(t,e){super(t),this.name="RootAccessEnablementError",this.partialSummary=e}}function g(o){return new r("Trusted access for iam.amazonaws.com is not enabled in Organizations. Re-run organisation setup \u2014 its enable-service-access phase enables it \u2014 or enable it manually (organizations:EnableAWSServiceAccess for iam.amazonaws.com) and retry enabling centralised root access.",o)}async function F(o,t){const e={enabled:[],alreadyEnabled:[]};let l;try{const n=await o.send(new h({}),{abortSignal:d(t)});l=new Set(n.EnabledFeatures??[])}catch(n){const i=u(n);return E(i)?a(new r("Access denied when listing organisation root-access features. Ensure your credentials have iam:ListOrganizationsFeatures permission.",e)):i==="ServiceAccessNotEnabledException"?a(g(e)):a(new r(`Failed to list organisation root-access features: ${p(b(n))}`,e))}const s=[];for(const n of y){if(R(t)){const c=s.length>0?`${s.join("; ")}; `:"";return a(new r(`${c}Aborted: centralised root-access enablement cancelled by shutdown signal.`,e))}if(l.has(n)){e.alreadyEnabled.push(n);continue}const{CommandClass:i,permission:f}=z[n];try{await o.send(new i({}),{abortSignal:d(t)}),e.enabled.push(n)}catch(c){const m=u(c);if(E(m)){const A=s.length>0?`${s.join("; ")}; `:"";return a(new r(`${A}Access denied when enabling ${n}. Ensure your credentials have ${f} permission.`,e))}if(m==="ServiceAccessNotEnabledException")return a(g(e));s.push(`${n}: ${p(b(c))}`)}}return s.length>0?a(new r(`Failed to enable centralised root access:
2
- ${s.join(`
3
- `)}`,e)):C(e)}export{y as ROOT_ACCESS_FEATURES,r as RootAccessEnablementError,F as enableCentralisedRootAccess};
1
+ var S=Object.defineProperty;var l=(s,a)=>S(s,"name",{value:a,configurable:!0});import{ListOrganizationsFeaturesCommand as w,EnableOrganizationsRootCredentialsManagementCommand as C,EnableOrganizationsRootSessionsCommand as R}from"@aws-sdk/client-iam";import{success as y,failure as o}from"@fjall/generator";import{composeSdkAbortSignal as u,extractErrorName as E,isAborted as z,isAccessDenied as b}from"./types.js";import{getErrorMessage as p,maskSensitiveOutput as g}from"@fjall/util";const O=["RootCredentialsManagement","RootSessions"],$={RootCredentialsManagement:{CommandClass:C,permission:"iam:EnableOrganizationsRootCredentialsManagement"},RootSessions:{CommandClass:R,permission:"iam:EnableOrganizationsRootSessions"}};class t extends Error{static{l(this,"RootAccessEnablementError")}partialSummary;constructor(a,e){super(a),this.name="RootAccessEnablementError",this.partialSummary=e}}function f(s){return new t("Trusted access for iam.amazonaws.com is not enabled in Organizations. Re-run organisation setup \u2014 its enable-service-access phase enables it \u2014 or enable it manually (organizations:EnableAWSServiceAccess for iam.amazonaws.com) and retry enabling centralised root access.",s)}l(f,"serviceAccessNotEnabledFailure");async function T(s,a){const e={enabled:[],alreadyEnabled:[]};let m;try{const n=await s.send(new w({}),{abortSignal:u(a)});m=new Set(n.EnabledFeatures??[])}catch(n){const i=E(n);return b(i)?o(new t("Access denied when listing organisation root-access features. Ensure your credentials have iam:ListOrganizationsFeatures permission.",e)):i==="ServiceAccessNotEnabledException"?o(f(e)):o(new t(`Failed to list organisation root-access features: ${g(p(n))}`,e))}const r=[];for(const n of O){if(z(a)){const c=r.length>0?`${r.join("; ")}; `:"";return o(new t(`${c}Aborted: centralised root-access enablement cancelled by shutdown signal.`,e))}if(m.has(n)){e.alreadyEnabled.push(n);continue}const{CommandClass:i,permission:A}=$[n];try{await s.send(new i({}),{abortSignal:u(a)}),e.enabled.push(n)}catch(c){const d=E(c);if(b(d)){const h=r.length>0?`${r.join("; ")}; `:"";return o(new t(`${h}Access denied when enabling ${n}. Ensure your credentials have ${A} permission.`,e))}if(d==="ServiceAccessNotEnabledException")return o(f(e));r.push(`${n}: ${g(p(c))}`)}}return r.length>0?o(new t(`Failed to enable centralised root access:
2
+ ${r.join(`
3
+ `)}`,e)):y(e)}l(T,"enableCentralisedRootAccess");export{O as ROOT_ACCESS_FEATURES,t as RootAccessEnablementError,T as enableCentralisedRootAccess};
@@ -1 +1 @@
1
- import{EnableAWSServiceAccessCommand as s}from"@aws-sdk/client-organizations";import{success as m,failure as e}from"@fjall/generator";import{extractErrorName as i,composeSdkAbortSignal as t,isAborted as w,AWS_ERROR_NAMES as l}from"./types.js";import{getErrorMessage as c}from"@fjall/util";const u=["account.amazonaws.com","sso.amazonaws.com","ipam.amazonaws.com","ram.amazonaws.com","backup.amazonaws.com","member.org.stacksets.cloudformation.amazonaws.com","guardduty.amazonaws.com","securityhub.amazonaws.com","config.amazonaws.com","inspector2.amazonaws.com","access-analyzer.amazonaws.com","cloudtrail.amazonaws.com","iam.amazonaws.com"];async function f(n,o){try{for(const a of u){if(w(o))return e(new Error("Aborted: service-access enablement cancelled by shutdown signal"));try{await n.send(new s({ServicePrincipal:a}),{abortSignal:t(o)})}catch(r){if(i(r)===l.ACCESS_DENIED)return e(new Error(`Access denied when enabling service access for ${a}. Ensure your credentials have organizations:EnableAWSServiceAccess permission.`));throw new Error(`Service principal ${a}: ${c(r)}`,{cause:r})}}return m(void 0)}catch(a){return e(new Error(`Failed to enable service access: ${c(a)}`))}}export{u as SERVICE_PRINCIPALS,f as enableServiceAccess};
1
+ var m=Object.defineProperty;var n=(e,r)=>m(e,"name",{value:r,configurable:!0});import{EnableAWSServiceAccessCommand as i}from"@aws-sdk/client-organizations";import{success as t,failure as c}from"@fjall/generator";import{extractErrorName as w,composeSdkAbortSignal as l,isAborted as u,AWS_ERROR_NAMES as d}from"./types.js";import{getErrorMessage as s}from"@fjall/util";const p=["account.amazonaws.com","sso.amazonaws.com","ipam.amazonaws.com","ram.amazonaws.com","backup.amazonaws.com","member.org.stacksets.cloudformation.amazonaws.com","guardduty.amazonaws.com","securityhub.amazonaws.com","config.amazonaws.com","inspector2.amazonaws.com","access-analyzer.amazonaws.com","cloudtrail.amazonaws.com","iam.amazonaws.com"];async function g(e,r){try{for(const a of p){if(u(r))return c(new Error("Aborted: service-access enablement cancelled by shutdown signal"));try{await e.send(new i({ServicePrincipal:a}),{abortSignal:l(r)})}catch(o){if(w(o)===d.ACCESS_DENIED)return c(new Error(`Access denied when enabling service access for ${a}. Ensure your credentials have organizations:EnableAWSServiceAccess permission.`));throw new Error(`Service principal ${a}: ${s(o)}`,{cause:o})}}return t(void 0)}catch(a){return c(new Error(`Failed to enable service access: ${s(a)}`))}}n(g,"enableServiceAccess");export{p as SERVICE_PRINCIPALS,g as enableServiceAccess};
@@ -1 +1 @@
1
- import{ActivateOrganizationsAccessCommand as o}from"@aws-sdk/client-cloudformation";import{success as a,failure as s}from"@fjall/generator";import{getErrorMessage as c}from"@fjall/util";import{composeSdkAbortSignal as i}from"./types.js";async function f(r,t){try{return await r.send(new o({}),{abortSignal:i(t)}),a(void 0)}catch(e){return s(new Error(`Failed to activate trusted access: ${c(e)}`))}}export{f as activateTrustedAccess};
1
+ var a=Object.defineProperty;var e=(r,t)=>a(r,"name",{value:t,configurable:!0});import{ActivateOrganizationsAccessCommand as s}from"@aws-sdk/client-cloudformation";import{success as c,failure as i}from"@fjall/generator";import{getErrorMessage as n}from"@fjall/util";import{composeSdkAbortSignal as m}from"./types.js";async function l(r,t){try{return await r.send(new s({}),{abortSignal:m(t)}),c(void 0)}catch(o){return i(new Error(`Failed to activate trusted access: ${n(o)}`))}}e(l,"activateTrustedAccess");export{l as activateTrustedAccess};
@@ -1 +1 @@
1
- const i=3e4,o={ACCESS_DENIED:"AccessDeniedException",ACCOUNT_ALREADY_REGISTERED:"AccountAlreadyRegisteredException",ACCOUNT_NOT_FOUND:"AccountNotFoundException",CHILD_NOT_FOUND:"ChildNotFoundException",ORGS_NOT_IN_USE:"AWSOrganizationsNotInUseException"};function c(n){return Array.isArray(n)}function r(n,e=3e4){const t=AbortSignal.timeout(e);return n!==void 0?AbortSignal.any([n,t]):t}function E(n){return n?.aborted===!0}function A(n){return typeof n=="object"&&n!==null&&"name"in n&&typeof n.name=="string"?n.name:"UnknownError"}function u(n){return n==="AccessDenied"||n===o.ACCESS_DENIED}export{o as AWS_ERROR_NAMES,i as SDK_TIMEOUT_MS,r as composeSdkAbortSignal,A as extractErrorName,E as isAborted,u as isAccessDenied,c as isOULeaf};
1
+ var i=Object.defineProperty;var t=(n,e)=>i(n,"name",{value:e,configurable:!0});const E=3e4,c={ACCESS_DENIED:"AccessDeniedException",ACCOUNT_ALREADY_REGISTERED:"AccountAlreadyRegisteredException",ACCOUNT_NOT_FOUND:"AccountNotFoundException",CHILD_NOT_FOUND:"ChildNotFoundException",ORGS_NOT_IN_USE:"AWSOrganizationsNotInUseException"};function A(n){return Array.isArray(n)}t(A,"isOULeaf");function u(n,e=3e4){const o=AbortSignal.timeout(e);return n!==void 0?AbortSignal.any([n,o]):o}t(u,"composeSdkAbortSignal");function s(n){return n?.aborted===!0}t(s,"isAborted");function _(n){return typeof n=="object"&&n!==null&&"name"in n&&typeof n.name=="string"?n.name:"UnknownError"}t(_,"extractErrorName");function S(n){return n==="AccessDenied"||n===c.ACCESS_DENIED}t(S,"isAccessDenied");export{c as AWS_ERROR_NAMES,E as SDK_TIMEOUT_MS,u as composeSdkAbortSignal,_ as extractErrorName,s as isAborted,S as isAccessDenied,A as isOULeaf};
@@ -1 +1 @@
1
- import{AccountClient as a,GetRegionOptStatusCommand as o}from"@aws-sdk/client-account";import{OPT_IN_REGION_CODES as r,optInRegionWarning as i}from"@fjall/util";import{composeSdkAbortSignal as s}from"../organisations/types.js";const c=1e4,l="us-east-1";async function A(n,e,t){if(!r.has(n))return"enabled";try{switch((await new a({region:l,credentials:e}).send(new o({RegionName:n}),{abortSignal:s(t,c)})).RegionOptStatus){case"ENABLED":case"ENABLED_BY_DEFAULT":return"enabled";case"DISABLED":case"DISABLING":return"disabled";case"ENABLING":return"enabling";default:return"unknown"}}catch{return"unknown"}}function S(n){return`Region ${n} is not enabled in this AWS account. ${i(n)} Enable it and wait for activation, then retry \u2014 or deploy to a region that is already enabled.`}export{A as checkRegionEnabled,S as regionDisabledMessage};
1
+ var o=Object.defineProperty;var t=(n,e)=>o(n,"name",{value:e,configurable:!0});import{AccountClient as r,GetRegionOptStatusCommand as i}from"@aws-sdk/client-account";import{OPT_IN_REGION_CODES as s,optInRegionWarning as c}from"@fjall/util";import{composeSdkAbortSignal as l}from"../organisations/types.js";const u=1e4,d="us-east-1";async function b(n,e,a){if(!s.has(n))return"enabled";try{switch((await new r({region:d,credentials:e}).send(new i({RegionName:n}),{abortSignal:l(a,u)})).RegionOptStatus){case"ENABLED":case"ENABLED_BY_DEFAULT":return"enabled";case"DISABLED":case"DISABLING":return"disabled";case"ENABLING":return"enabling";default:return"unknown"}}catch{return"unknown"}}t(b,"checkRegionEnabled");function I(n){return`Region ${n} is not enabled in this AWS account. ${c(n)} Enable it and wait for activation, then retry \u2014 or deploy to a region that is already enabled.`}t(I,"regionDisabledMessage");export{b as checkRegionEnabled,I as regionDisabledMessage};
@@ -1 +1 @@
1
- import{AssumeRootCommand as m}from"@aws-sdk/client-sts";import{success as S,failure as n}from"@fjall/generator";import{getErrorMessage as f,maskSensitiveOutput as A}from"@fjall/util";import{composeSdkAbortSignal as p,extractErrorName as w,isAccessDenied as y}from"../organisations/types.js";const d=["arn:aws:iam::aws:policy/root-task/IAMAuditRootUserCredentials","arn:aws:iam::aws:policy/root-task/IAMCreateRootUserPassword","arn:aws:iam::aws:policy/root-task/IAMDeleteRootUserCredentials","arn:aws:iam::aws:policy/root-task/S3UnlockBucketPolicy","arn:aws:iam::aws:policy/root-task/SQSUnlockQueuePolicy"],g=new Set(d);function k(t){return g.has(t)}const u=900,T=/^\d{12}$/;async function O(t,i){const{targetAccountId:r,taskPolicyArn:a,durationSeconds:o,abortSignal:l}=i;if(!T.test(r))return n(new Error(`Invalid target account id "${r}": expected a 12-digit AWS account id.`));if(!k(a))return n(new Error(`Task policy "${a}" is not an AWS root-task policy. AssumeRoot accepts only: ${d.join(", ")}.`));if(o!==void 0&&(!Number.isInteger(o)||o<1||o>u))return n(new Error(`Invalid durationSeconds ${o}: AssumeRoot sessions must be a whole number of seconds between 1 and ${u}.`));const c=await R(t);if(c===void 0||c==="")return n(new Error("AssumeRoot requires a regional STS endpoint, but the client has no resolvable region. Construct the STS client with an explicit region."));try{const s=await t.send(new m({TargetPrincipal:r,TaskPolicyArn:{arn:a},...o!==void 0&&{DurationSeconds:o}}),{abortSignal:p(l)}),e=s.Credentials;return e?.AccessKeyId===void 0||e.SecretAccessKey===void 0||e.SessionToken===void 0?n(new Error(`AssumeRoot for account ${r} returned no credentials.`)):S({credentials:{accessKeyId:e.AccessKeyId,secretAccessKey:e.SecretAccessKey,sessionToken:e.SessionToken,...e.Expiration!==void 0&&{expiration:e.Expiration}},...s.SourceIdentity!==void 0&&{sourceIdentity:s.SourceIdentity}})}catch(s){const e=w(s);return y(e)?n(new Error(`Access denied assuming root for account ${r}. Ensure the credentials belong to the management account (or the delegated admin) with sts:AssumeRoot permission, and that centralised root access management is enabled for the organisation.`)):n(new Error(`Failed to assume root for account ${r}: ${A(f(s))}`))}}async function R(t){try{const i=t.config.region;return typeof i=="function"?await i():i}catch{return}}export{u as MAX_ROOT_SESSION_SECONDS,d as ROOT_TASK_POLICY_ARNS,O as assumeRootForTask,k as isRootTaskPolicyArn};
1
+ var S=Object.defineProperty;var a=(o,n)=>S(o,"name",{value:n,configurable:!0});import{AssumeRootCommand as f}from"@aws-sdk/client-sts";import{success as A,failure as r}from"@fjall/generator";import{getErrorMessage as p,maskSensitiveOutput as w}from"@fjall/util";import{composeSdkAbortSignal as y,extractErrorName as g,isAccessDenied as k}from"../organisations/types.js";const u=["arn:aws:iam::aws:policy/root-task/IAMAuditRootUserCredentials","arn:aws:iam::aws:policy/root-task/IAMCreateRootUserPassword","arn:aws:iam::aws:policy/root-task/IAMDeleteRootUserCredentials","arn:aws:iam::aws:policy/root-task/S3UnlockBucketPolicy","arn:aws:iam::aws:policy/root-task/SQSUnlockQueuePolicy"],T=new Set(u);function R(o){return T.has(o)}a(R,"isRootTaskPolicyArn");const l=900,I=/^\d{12}$/;async function P(o,n){const{targetAccountId:s,taskPolicyArn:c,durationSeconds:t,abortSignal:m}=n;if(!I.test(s))return r(new Error(`Invalid target account id "${s}": expected a 12-digit AWS account id.`));if(!R(c))return r(new Error(`Task policy "${c}" is not an AWS root-task policy. AssumeRoot accepts only: ${u.join(", ")}.`));if(t!==void 0&&(!Number.isInteger(t)||t<1||t>l))return r(new Error(`Invalid durationSeconds ${t}: AssumeRoot sessions must be a whole number of seconds between 1 and ${l}.`));const d=await E(o);if(d===void 0||d==="")return r(new Error("AssumeRoot requires a regional STS endpoint, but the client has no resolvable region. Construct the STS client with an explicit region."));try{const i=await o.send(new f({TargetPrincipal:s,TaskPolicyArn:{arn:c},...t!==void 0&&{DurationSeconds:t}}),{abortSignal:y(m)}),e=i.Credentials;return e?.AccessKeyId===void 0||e.SecretAccessKey===void 0||e.SessionToken===void 0?r(new Error(`AssumeRoot for account ${s} returned no credentials.`)):A({credentials:{accessKeyId:e.AccessKeyId,secretAccessKey:e.SecretAccessKey,sessionToken:e.SessionToken,...e.Expiration!==void 0&&{expiration:e.Expiration}},...i.SourceIdentity!==void 0&&{sourceIdentity:i.SourceIdentity}})}catch(i){const e=g(i);return k(e)?r(new Error(`Access denied assuming root for account ${s}. Ensure the credentials belong to the management account (or the delegated admin) with sts:AssumeRoot permission, and that centralised root access management is enabled for the organisation.`)):r(new Error(`Failed to assume root for account ${s}: ${w(p(i))}`))}}a(P,"assumeRootForTask");async function E(o){try{const n=o.config.region;return typeof n=="function"?await n():n}catch{return}}a(E,"resolveClientRegion");export{l as MAX_ROOT_SESSION_SECONDS,u as ROOT_TASK_POLICY_ARNS,P as assumeRootForTask,R as isRootTaskPolicyArn};
@@ -53,17 +53,20 @@ export declare function buildTargetUnreadyAdvisory(params: {
53
53
  providerAccounts?: OrgConfig["providerAccounts"];
54
54
  }): string;
55
55
  /**
56
- * Probe whether the `Account` CloudFormation stack exists and is provisioned
57
- * in `region`. `success(true)` = a usable stack; `success(false)` = absent or
56
+ * Probe whether the governance CloudFormation stack (`Account` by default;
57
+ * pass `stackName` for the platform-tier `Platform` shape) exists and is
58
+ * provisioned in `region`. `success(true)` = a usable stack; `success(false)` = absent or
58
59
  * an unprovisioned shell (failed/rolled-back create, unexecuted change-set,
59
60
  * in-flight/failed delete); `failure` = the describe itself errored. Exported
60
61
  * so connect-time deploy-state probes reuse the same unprovisioned-status
61
62
  * interpretation as the deploy readiness path rather than re-deriving it.
62
63
  */
63
- export declare function probeAccountStackExists(client: CloudFormationClient, region: string, abortSignal?: AbortSignal): Promise<Result<boolean>>;
64
+ export declare function probeAccountStackExists(client: CloudFormationClient, region: string, abortSignal?: AbortSignal, stackName?: string): Promise<Result<boolean>>;
64
65
  /**
65
66
  * Probe whether a deploy target is provisioned before any AWS mutation:
66
- * the fixed-name Account stack must exist in the target region (both shapes),
67
+ * the governance stack must exist in the target region (`Account` for member
68
+ * and solo accounts, `Platform` for a platform-tier target — its substrate
69
+ * deploys under that name via `Platform extends Account`),
67
70
  * and for org estates (a tier-organisation account in providerAccounts) the
68
71
  * (account, region) IPAM pool must exist. Bootstrap is excluded — both deploy
69
72
  * paths self-heal it.
@@ -1 +1 @@
1
- import{DescribeStacksCommand as N}from"@aws-sdk/client-cloudformation";import{DescribeIpamPoolsCommand as S}from"@aws-sdk/client-ec2";import{success as i,failure as c}from"@fjall/generator";import{accountTier as A,generateTargetName as g,getErrorMessage as E}from"@fjall/util";import{formatIpamPairTagValue as y,IPAM_OPERATIONS_POOL_TAG_KEY as O,STACK_NOT_FOUND_PATTERN as k}from"@fjall/util/aws";import{getOrganisationStackName as _,ORGANISATION_TYPES as R}from"../types/operations.js";import{hasOrganisationTierAccount as T}from"./organisations/accountGlobals.js";import{composeSdkAbortSignal as I,isAborted as P}from"./organisations/types.js";const m=_(R.ACCOUNT),w=new Set(["CREATE_FAILED","ROLLBACK_IN_PROGRESS","ROLLBACK_COMPLETE","ROLLBACK_FAILED","REVIEW_IN_PROGRESS","DELETE_IN_PROGRESS","DELETE_FAILED"]);function $(e){switch(e.kind){case"missing-account-stack":return`the ${e.stackName} stack was not found in ${e.region}`;case"missing-ipam-pool":return`no IPAM pool exists for account ${e.accountId} in ${e.region}`}}function L(e){const t=e.accountName??e.accountId,n=e.reasons.map($).join(" and "),o=e.accountName!==void 0?`run \`fjall target set ${g(e.accountName,e.region)}\` then \`fjall deploy account\`, then retry this deploy`:"run `fjall target list` to find the target name, `fjall target set <target>`, then `fjall deploy account`, then retry this deploy",r=T(e.providerAccounts)?"run `fjall deploy organisation` (the cascade provisions every account and creates the IPAM pool), then retry this deploy":o;return`Target "${t}" is not ready in ${e.region}: ${n}. Nothing has been deployed. Provision the target first: ${r}.`}function b(e){return e instanceof Error&&e.name==="ValidationError"&&e.message.includes(k)}async function C(e,t,n){try{const r=(await e.send(new N({StackName:m}),{abortSignal:I(n)})).Stacks?.[0]?.StackStatus;return i(r!==void 0&&!w.has(r))}catch(o){return b(o)?i(!1):c(new Error(`Failed to probe the ${m} stack in ${t}: ${E(o)}`))}}function x(e,t,n,o){return e.Locale!==n?!1:o.side==="owner"?(e.Tags??[]).some(r=>r.Key===O&&r.Value===o.pairTagValue):e.OwnerId===t?!1:o.platformAccountId===void 0||e.OwnerId===o.platformAccountId}async function v(e,t,n,o,r){try{let a;do{if(P(r))return c(new Error(`Aborted: IPAM pool probe for account ${t} in ${n} cancelled by shutdown signal`));const s=await e.send(new S({...a!==void 0&&{NextToken:a}}),{abortSignal:I(r)});if((s.IpamPools??[]).some(u=>x(u,t,n,o)))return i(!0);a=s.NextToken}while(a!==void 0);return i(!1)}catch(a){return c(new Error(`Failed to probe IPAM pools for account ${t} in ${n}: ${E(a)}`))}}async function B(e,t,n,o,r){const a=[],s=await C(e.cloudFormation,n,r);if(!s.success)return c(s.error);s.data||a.push({kind:"missing-account-stack",stackName:m,region:n});const d=o?.providerAccounts;if(T(d)){const u=(d??[]).find(l=>l.id===t.id),p=(d??[]).find(l=>A(l)==="platform")?.id,h=u!==void 0&&A(u)==="platform"?{side:"owner",pairTagValue:y(t.id,n)}:{side:"consumer",...p!==void 0&&{platformAccountId:p}},f=await v(e.ec2,t.id,n,h,r);if(!f.success)return c(f.error);f.data||a.push({kind:"missing-ipam-pool",accountId:t.id,region:n})}return a.length===0?i({ready:!0}):i({ready:!1,reasons:a,advisory:L({accountId:t.id,...t.name!==void 0&&{accountName:t.name},region:n,reasons:a,...o!==void 0&&{providerAccounts:o.providerAccounts}})})}export{m as ACCOUNT_STACK_NAME,L as buildTargetUnreadyAdvisory,B as checkTargetReadiness,$ as describeTargetReadinessReason,C as probeAccountStackExists};
1
+ var _=Object.defineProperty;var s=(e,t)=>_(e,"name",{value:t,configurable:!0});import{DescribeStacksCommand as P}from"@aws-sdk/client-cloudformation";import{DescribeIpamPoolsCommand as R}from"@aws-sdk/client-ec2";import{success as c,failure as d}from"@fjall/generator";import{accountTier as E,generateTargetName as k,getErrorMessage as T}from"@fjall/util";import{formatIpamPairTagValue as w,IPAM_OPERATIONS_POOL_TAG_KEY as $,STACK_NOT_FOUND_PATTERN as L}from"@fjall/util/aws";import{getOrganisationStackName as I,ORGANISATION_TYPES as S}from"../types/operations.js";import{hasOrganisationTierAccount as h}from"./organisations/accountGlobals.js";import{composeSdkAbortSignal as N,isAborted as C}from"./organisations/types.js";const g=I(S.ACCOUNT),b=I(S.PLATFORM),v=new Set(["CREATE_FAILED","ROLLBACK_IN_PROGRESS","ROLLBACK_COMPLETE","ROLLBACK_FAILED","REVIEW_IN_PROGRESS","DELETE_IN_PROGRESS","DELETE_FAILED"]);function x(e){switch(e.kind){case"missing-account-stack":return`the ${e.stackName} stack was not found in ${e.region}`;case"missing-ipam-pool":return`no IPAM pool exists for account ${e.accountId} in ${e.region}`}}s(x,"describeTargetReadinessReason");function M(e){const t=e.accountName??e.accountId,n=e.reasons.map(x).join(" and "),o=e.accountName!==void 0?`run \`fjall target set ${k(e.accountName,e.region)}\` then \`fjall deploy account\`, then retry this deploy`:"run `fjall target list` to find the target name, `fjall target set <target>`, then `fjall deploy account`, then retry this deploy",a=h(e.providerAccounts)?"run `fjall deploy organisation` (the cascade provisions every account and creates the IPAM pool), then retry this deploy":o;return`Target "${t}" is not ready in ${e.region}: ${n}. Nothing has been deployed. Provision the target first: ${a}.`}s(M,"buildTargetUnreadyAdvisory");function F(e){return e instanceof Error&&e.name==="ValidationError"&&e.message.includes(L)}s(F,"isStackNotFoundError");async function D(e,t,n,o=g){try{const r=(await e.send(new P({StackName:o}),{abortSignal:N(n)})).Stacks?.[0]?.StackStatus;return c(r!==void 0&&!v.has(r))}catch(a){return F(a)?c(!1):d(new Error(`Failed to probe the ${o} stack in ${t}: ${T(a)}`))}}s(D,"probeAccountStackExists");function K(e,t,n,o){return e.Locale!==n?!1:o.side==="owner"?(e.Tags??[]).some(a=>a.Key===$&&a.Value===o.pairTagValue):e.OwnerId===t?!1:o.platformAccountId===void 0||e.OwnerId===o.platformAccountId}s(K,"matchesIpamPair");async function j(e,t,n,o,a){try{let r;do{if(C(a))return d(new Error(`Aborted: IPAM pool probe for account ${t} in ${n} cancelled by shutdown signal`));const i=await e.send(new R({...r!==void 0&&{NextToken:r}}),{abortSignal:N(a)});if((i.IpamPools??[]).some(u=>K(u,t,n,o)))return c(!0);r=i.NextToken}while(r!==void 0);return c(!1)}catch(r){return d(new Error(`Failed to probe IPAM pools for account ${t} in ${n}: ${T(r)}`))}}s(j,"probeIpamPoolExists");async function J(e,t,n,o,a){const r=[],i=o?.providerAccounts,l=(i??[]).find(f=>f.id===t.id),u=l!==void 0&&E(l)==="platform",A=u?b:g,m=await D(e.cloudFormation,n,a,A);if(!m.success)return d(m.error);if(m.data||r.push({kind:"missing-account-stack",stackName:A,region:n}),h(i)){const f=(i??[]).find(O=>E(O)==="platform")?.id,y=u?{side:"owner",pairTagValue:w(t.id,n)}:{side:"consumer",...f!==void 0&&{platformAccountId:f}},p=await j(e.ec2,t.id,n,y,a);if(!p.success)return d(p.error);p.data||r.push({kind:"missing-ipam-pool",accountId:t.id,region:n})}return r.length===0?c({ready:!0}):c({ready:!1,reasons:r,advisory:M({accountId:t.id,...t.name!==void 0&&{accountName:t.name},region:n,reasons:r,...o!==void 0&&{providerAccounts:o.providerAccounts}})})}s(J,"checkTargetReadiness");export{g as ACCOUNT_STACK_NAME,M as buildTargetUnreadyAdvisory,J as checkTargetReadiness,x as describeTargetReadinessReason,D as probeAccountStackExists};
@@ -1 +1 @@
1
- import{hasOrganisationTierAccount as t}from"./organisations/accountGlobals.js";import{describeTargetReadinessReason as r}from"./targetReadiness.js";function s(o){const n=o.accountName??o.accountId,e=o.reasons.map(r).join(" and "),i=t(o.providerAccounts)?"Run `fjall deploy organisation` \u2014 the cascade provisions every account and creates the IPAM pool":"Run `fjall deploy account` to provision it";return`Target "${n}" is not ready in ${o.region}: ${e}. ${i}.`}export{s as buildTargetSetUnreadyAdvisory};
1
+ var r=Object.defineProperty;var e=(o,n)=>r(o,"name",{value:n,configurable:!0});import{hasOrganisationTierAccount as a}from"./organisations/accountGlobals.js";import{describeTargetReadinessReason as c}from"./targetReadiness.js";function l(o){const n=o.accountName??o.accountId,i=o.reasons.map(c).join(" and "),t=a(o.providerAccounts)?"Run `fjall deploy organisation` \u2014 the cascade provisions every account and creates the IPAM pool":"Run `fjall deploy account` to provision it";return`Target "${n}" is not ready in ${o.region}: ${i}. ${t}.`}e(l,"buildTargetSetUnreadyAdvisory");export{l as buildTargetSetUnreadyAdvisory};
@@ -1 +1 @@
1
- function i(r){if(!r.startsWith("arn:"))return;const e=r.split(":");if(!(e.length<6))return{partition:e[1]??"",service:e[2]??"",region:e[3]??"",accountId:e[4]??"",resource:e.slice(5).join(":")}}function u(r,e,n,t,c="aws"){return`arn:${c}:${r}:${e}:${n}:${t}`}function s(r){const e=i(r);if(!e||e.service!=="ecs")return;const n=e.resource.match(/^service\/([^/]+)\/(.+)$/);if(n)return{clusterName:n[1]??"",serviceName:n[2]??"",region:e.region,accountId:e.accountId}}function o(r){return s(r)?.serviceName}export{u as buildArn,o as extractEcsServiceName,s as extractEcsServiceParts,i as parseArn};
1
+ var s=Object.defineProperty;var t=(r,e)=>s(r,"name",{value:e,configurable:!0});function u(r){if(!r.startsWith("arn:"))return;const e=r.split(":");if(!(e.length<6))return{partition:e[1]??"",service:e[2]??"",region:e[3]??"",accountId:e[4]??"",resource:e.slice(5).join(":")}}t(u,"parseArn");function d(r,e,n,c,i="aws"){return`arn:${i}:${r}:${e}:${n}:${c}`}t(d,"buildArn");function o(r){const e=u(r);if(!e||e.service!=="ecs")return;const n=e.resource.match(/^service\/([^/]+)\/(.+)$/);if(n)return{clusterName:n[1]??"",serviceName:n[2]??"",region:e.region,accountId:e.accountId}}t(o,"extractEcsServiceParts");function f(r){return o(r)?.serviceName}t(f,"extractEcsServiceName");export{d as buildArn,f as extractEcsServiceName,o as extractEcsServiceParts,u as parseArn};
@@ -1 +1 @@
1
- function n(t){return t instanceof Error&&typeof t.name=="string"}function a(t){return n(t)?"$metadata"in t||"statusCode"in t:!1}function u(t){if(!(typeof t!="object"||t===null)){if("name"in t&&typeof t.name=="string")return t.name;if("Code"in t&&typeof t.Code=="string")return t.Code}}function o(t){const s=u(t)??"UnknownError",i=t instanceof Error?t.message:String(t);let e;return a(t)&&(e=t.$metadata?.httpStatusCode??t.statusCode),{message:i,name:s,httpCode:e}}function d(t){return n(t)&&t.name==="AccessDeniedException"}function f(t){return n(t)&&t.name==="ImageAlreadyExistsException"}export{o as getAwsErrorDetails,u as getAwsErrorName,a as hasAwsSdkMetadata,d as isAccessDeniedError,n as isAwsSdkV3Error,f as isImageAlreadyExistsError};
1
+ var u=Object.defineProperty;var n=(t,e)=>u(t,"name",{value:e,configurable:!0});function s(t){return t instanceof Error&&typeof t.name=="string"}n(s,"isAwsSdkV3Error");function o(t){return s(t)?"$metadata"in t||"statusCode"in t:!1}n(o,"hasAwsSdkMetadata");function d(t){if(!(typeof t!="object"||t===null)){if("name"in t&&typeof t.name=="string")return t.name;if("Code"in t&&typeof t.Code=="string")return t.Code}}n(d,"getAwsErrorName");function c(t){const e=d(t)??"UnknownError",a=t instanceof Error?t.message:String(t);let i;return o(t)&&(i=t.$metadata?.httpStatusCode??t.statusCode),{message:a,name:e,httpCode:i}}n(c,"getAwsErrorDetails");function m(t){return s(t)&&t.name==="AccessDeniedException"}n(m,"isAccessDeniedError");function p(t){return s(t)&&t.name==="ImageAlreadyExistsException"}n(p,"isImageAlreadyExistsError");export{c as getAwsErrorDetails,d as getAwsErrorName,o as hasAwsSdkMetadata,m as isAccessDeniedError,s as isAwsSdkV3Error,p as isImageAlreadyExistsError};
@@ -1 +1 @@
1
- import{CloudFormationClient as g,DescribeStackEventsCommand as P,DescribeStacksCommand as k}from"@aws-sdk/client-cloudformation";import{logger as T}from"@fjall/util/logger";import{getErrorMessage as f,maskSensitiveOutput as A}from"@fjall/util";import{STACK_NOT_FOUND_PATTERN as S,maskFailureAnalysis as h}from"@fjall/util/aws";import{CF_STACK_RESOURCE_TYPE as R}from"./cloudformationEventTypes.js";import{composeSdkAbortSignal as I,extractErrorName as L}from"../organisations/types.js";const _=["CREATE_COMPLETE","CREATE_FAILED","DELETE_COMPLETE","DELETE_FAILED","UPDATE_COMPLETE","UPDATE_FAILED","UPDATE_ROLLBACK_COMPLETE","UPDATE_ROLLBACK_FAILED","ROLLBACK_COMPLETE","ROLLBACK_FAILED","IMPORT_COMPLETE","IMPORT_ROLLBACK_COMPLETE","IMPORT_ROLLBACK_FAILED"],M=["CREATE_COMPLETE","UPDATE_COMPLETE","DELETE_COMPLETE","IMPORT_COMPLETE"];function O(t){return _.includes(t)}function $(t){return M.includes(t)}const F=new Set(["AWS::EC2::IPAMPool","AWS::EC2::IPAMPoolCidr","AWS::EC2::IPAM"]);function D(t){return F.has(t)}class H{inProgress=new Map;get size(){return this.inProgress.size}clear(){this.inProgress.clear()}trackEvent(s,c){if(s.status.includes("IN_PROGRESS")){this.inProgress.set(s.logicalId,{resourceType:s.resourceType,startTime:s.timestamp});const n=Array.from(this.inProgress.entries()).map(([r,i])=>`${r} (${i.resourceType.split("::").pop()}, started ${i.startTime.toISOString()})`);T.debug("CloudFormation","IPAM resource started",{logicalId:s.logicalId,resourceType:s.resourceType,concurrentIpamCount:this.inProgress.size,concurrentIpamResources:n}),c&&c.writeEvent(s,{phase:"ipam_concurrency",isMainStack:!0,parentStack:`concurrent=${this.inProgress.size}: ${n.join(", ")}`})}else if(s.status.includes("COMPLETE")||s.status.includes("FAILED")){const n=this.inProgress.has(s.logicalId),r=this.inProgress.get(s.logicalId);this.inProgress.delete(s.logicalId);const i=r?s.timestamp.getTime()-r.startTime.getTime():void 0;if(T.debug("CloudFormation",`IPAM resource ${s.status}`,{logicalId:s.logicalId,resourceType:s.resourceType,status:s.status,statusReason:s.statusReason,durationMs:i,wasTracked:n,remainingIpamCount:this.inProgress.size,remainingIpamResources:Array.from(this.inProgress.keys())}),s.status.includes("FAILED")&&c){const a=Array.from(this.inProgress.entries()).map(([l,o])=>({logicalId:l,resourceType:o.resourceType,startTime:o.startTime.toISOString(),inFlightDurationMs:s.timestamp.getTime()-o.startTime.getTime()}));c.writeEvent(s,{phase:"ipam_failure_snapshot",isMainStack:!0,parentStack:JSON.stringify({failedResource:s.logicalId,reason:s.statusReason,durationMs:i,concurrentIpamAtFailure:a})})}}}}const B=12e4;async function W(t,s,c){try{const n=t.aws.getClient(g);let r,i=!1,a=!1;do{const l=new P({StackName:s,...r?{NextToken:r}:{}}),o=await n.send(l,{abortSignal:I(void 0)});if(!o.StackEvents)break;for(const e of o.StackEvents){const u=`${e.EventId}`;if(t.seenEventIds.has(u)){a=!0;continue}if(t.seenEventIds.add(u),e.Timestamp!==void 0&&e.Timestamp.getTime()<t.eventCutoffMs){a=!0;continue}if(e.ResourceType===R&&e.LogicalResourceId!==s){const E=e.ResourceStatus||"",p=e.LogicalResourceId||"";E.includes("IN_PROGRESS")?t.activeNestedStacks.set(p,E):(E.includes("COMPLETE")||E.includes("FAILED"))&&t.activeNestedStacks.delete(p)}if(e.LogicalResourceId===s&&e.ResourceType===R){const E=e.ResourceStatus||"";O(E)&&(i=!0)}e.ResourceStatus&&e.ResourceStatus.includes("FAILED")&&e.ResourceStatusReason&&(t.failureReasons.set(e.LogicalResourceId||"unknown",e.ResourceStatusReason),T.debug("CloudFormation",`Captured failure: ${e.LogicalResourceId} - ${e.ResourceStatusReason}`));const d=e.Timestamp===void 0||e.Timestamp.getTime()>=t.tokenLatchCutoffMs,m={logicalId:e.LogicalResourceId||"",physicalId:e.PhysicalResourceId,resourceType:e.ResourceType||"",status:e.ResourceStatus||"",statusReason:e.ResourceStatusReason,timestamp:e.Timestamp||new Date,clientRequestToken:d?e.ClientRequestToken:void 0},C=m.logicalId,y=t.eventHistory.get(C)??[];if(y.push(m),t.eventHistory.set(C,y),t.eventHistory.size>t.maxHistorySize){const E=[...t.eventHistory.keys()].slice(0,t.eventHistory.size-t.maxHistorySize);for(const p of E)t.eventHistory.delete(p)}D(m.resourceType)&&t.ipamTracker.trackEvent(m,t.eventLogger),t.eventLogger&&t.eventLogger.writeEvent(m),T.debug("CloudFormation","pollEvents delivering event",{stackName:s,logicalId:m.logicalId,status:m.status,resourceType:m.resourceType}),c(m)}r=a?void 0:o.NextToken}while(r);return i&&t.activeNestedStacks.size>0?!1:i}catch(n){const r=L(n),i=f(n);return r==="ValidationError"&&i.includes(S)?!1:r==="Throttling"||r==="TooManyRequestsException"||r==="ThrottlingException"?"throttled":(i.includes(S)||T.error("CloudFormation","Unexpected polling error",{error:A(i)}),!1)}}async function q(t,s,c,n,r,i){const a=await w(t,s),l=a?.status||"UNKNOWN",o=l.endsWith("_COMPLETE")&&!l.includes("ROLLBACK")&&!l.includes("FAILED");let e=a?.statusReason;!o&&c.size>0&&(e=(Array.from(c.values())[0]??e)||e);let u=null;if(!o&&r){const d=r.analyseFailure(i,s);u=d===null?null:h(d)}return n&&(n.writeDeploymentEnd(o,l,e),!o&&c.size>0&&n.writeFailureSummary(Array.from(c.entries()).map(([d,m])=>({logicalId:d,reason:m}))),u&&n.writeFailureAnalysis({rootCause:u.rootCause.reason,failedResources:u.affectedResources.map(d=>({logicalId:d.logicalId,resourceType:d.resourceType,reason:d.statusReason||"Unknown reason"})),dependencyChain:u.dependencyChain,remediation:u.remediation})),{success:o,failureMessage:e,analysis:u}}async function w(t,s,c){try{const n=new k({StackName:s}),a=(await t.getClient(g).send(n,{abortSignal:I(c)})).Stacks?.[0];return a?{status:a.StackStatus||"UNKNOWN",statusReason:a.StackStatusReason}:null}catch(n){return T.debug("CloudFormation","getStackStatus failed",{error:f(n)}),null}}async function V(t,s,c){const n=[];try{const r=new P({StackName:s}),a=await t.getClient(g).send(r,{abortSignal:I(c)});if(!a.StackEvents)return n;const l=new Map;for(const o of a.StackEvents){const e=o.LogicalResourceId||"";if(e===s&&o.ResourceType===R)continue;const u=l.get(e);(!u||o.Timestamp&&u.timestamp<o.Timestamp)&&l.set(e,{logicalId:e,physicalId:o.PhysicalResourceId,resourceType:o.ResourceType||"",status:o.ResourceStatus||"",statusReason:o.ResourceStatusReason,timestamp:o.Timestamp||new Date,clientRequestToken:o.ClientRequestToken})}return Array.from(l.values())}catch(r){const i=L(r),a=f(r);return i==="ValidationError"&&a.includes(S)||a.includes(S)||T.error("CloudFormation","Error getting current resources",{error:A(f(r))}),n}}export{B as EVENT_CUTOFF_SKEW_MS,H as IpamConcurrencyTracker,V as fetchCurrentResources,w as fetchStackStatus,q as handleStackCompletion,D as isIpamResource,$ as isSuccessState,O as isTerminalState,W as pollStackEvents};
1
+ var h=Object.defineProperty;var E=(t,e)=>h(t,"name",{value:e,configurable:!0});import{CloudFormationClient as R,DescribeStackEventsCommand as A,DescribeStacksCommand as _}from"@aws-sdk/client-cloudformation";import{logger as p}from"@fjall/util/logger";import{getErrorMessage as S,maskSensitiveOutput as L}from"@fjall/util";import{STACK_NOT_FOUND_PATTERN as g,maskFailureAnalysis as M}from"@fjall/util/aws";import{CF_STACK_RESOURCE_TYPE as I}from"./cloudformationEventTypes.js";import{composeSdkAbortSignal as C,extractErrorName as k}from"../organisations/types.js";const O=["CREATE_COMPLETE","CREATE_FAILED","DELETE_COMPLETE","DELETE_FAILED","UPDATE_COMPLETE","UPDATE_FAILED","UPDATE_ROLLBACK_COMPLETE","UPDATE_ROLLBACK_FAILED","ROLLBACK_COMPLETE","ROLLBACK_FAILED","IMPORT_COMPLETE","IMPORT_ROLLBACK_COMPLETE","IMPORT_ROLLBACK_FAILED"],F=["CREATE_COMPLETE","UPDATE_COMPLETE","DELETE_COMPLETE","IMPORT_COMPLETE"];function D(t){return O.includes(t)}E(D,"isTerminalState");function W(t){return F.includes(t)}E(W,"isSuccessState");const w=new Set(["AWS::EC2::IPAMPool","AWS::EC2::IPAMPoolCidr","AWS::EC2::IPAM"]);function N(t){return w.has(t)}E(N,"isIpamResource");class q{static{E(this,"IpamConcurrencyTracker")}inProgress=new Map;get size(){return this.inProgress.size}clear(){this.inProgress.clear()}trackEvent(e,c){if(e.status.includes("IN_PROGRESS")){this.inProgress.set(e.logicalId,{resourceType:e.resourceType,startTime:e.timestamp});const n=Array.from(this.inProgress.entries()).map(([r,i])=>`${r} (${i.resourceType.split("::").pop()}, started ${i.startTime.toISOString()})`);p.debug("CloudFormation","IPAM resource started",{logicalId:e.logicalId,resourceType:e.resourceType,concurrentIpamCount:this.inProgress.size,concurrentIpamResources:n}),c&&c.writeEvent(e,{phase:"ipam_concurrency",isMainStack:!0,parentStack:`concurrent=${this.inProgress.size}: ${n.join(", ")}`})}else if(e.status.includes("COMPLETE")||e.status.includes("FAILED")){const n=this.inProgress.has(e.logicalId),r=this.inProgress.get(e.logicalId);this.inProgress.delete(e.logicalId);const i=r?e.timestamp.getTime()-r.startTime.getTime():void 0;if(p.debug("CloudFormation",`IPAM resource ${e.status}`,{logicalId:e.logicalId,resourceType:e.resourceType,status:e.status,statusReason:e.statusReason,durationMs:i,wasTracked:n,remainingIpamCount:this.inProgress.size,remainingIpamResources:Array.from(this.inProgress.keys())}),e.status.includes("FAILED")&&c){const a=Array.from(this.inProgress.entries()).map(([l,o])=>({logicalId:l,resourceType:o.resourceType,startTime:o.startTime.toISOString(),inFlightDurationMs:e.timestamp.getTime()-o.startTime.getTime()}));c.writeEvent(e,{phase:"ipam_failure_snapshot",isMainStack:!0,parentStack:JSON.stringify({failedResource:e.logicalId,reason:e.statusReason,durationMs:i,concurrentIpamAtFailure:a})})}}}}const V=12e4;async function j(t,e,c){try{const n=t.aws.getClient(R);let r,i=!1,a=!1;do{const l=new A({StackName:e,...r?{NextToken:r}:{}}),o=await n.send(l,{abortSignal:C(void 0)});if(!o.StackEvents)break;for(const s of o.StackEvents){const u=`${s.EventId}`;if(t.seenEventIds.has(u)){a=!0;continue}if(t.seenEventIds.add(u),s.Timestamp!==void 0&&s.Timestamp.getTime()<t.eventCutoffMs){a=!0;continue}if(s.ResourceType===I&&s.LogicalResourceId!==e){const T=s.ResourceStatus||"",f=s.LogicalResourceId||"";T.includes("IN_PROGRESS")?t.activeNestedStacks.set(f,T):(T.includes("COMPLETE")||T.includes("FAILED"))&&t.activeNestedStacks.delete(f)}if(s.LogicalResourceId===e&&s.ResourceType===I){const T=s.ResourceStatus||"";D(T)&&(i=!0)}s.ResourceStatus&&s.ResourceStatus.includes("FAILED")&&s.ResourceStatusReason&&(t.failureReasons.set(s.LogicalResourceId||"unknown",s.ResourceStatusReason),p.debug("CloudFormation",`Captured failure: ${s.LogicalResourceId} - ${s.ResourceStatusReason}`));const d=s.Timestamp===void 0||s.Timestamp.getTime()>=t.tokenLatchCutoffMs,m={logicalId:s.LogicalResourceId||"",physicalId:s.PhysicalResourceId,resourceType:s.ResourceType||"",status:s.ResourceStatus||"",statusReason:s.ResourceStatusReason,timestamp:s.Timestamp||new Date,clientRequestToken:d?s.ClientRequestToken:void 0},y=m.logicalId,P=t.eventHistory.get(y)??[];if(P.push(m),t.eventHistory.set(y,P),t.eventHistory.size>t.maxHistorySize){const T=[...t.eventHistory.keys()].slice(0,t.eventHistory.size-t.maxHistorySize);for(const f of T)t.eventHistory.delete(f)}N(m.resourceType)&&t.ipamTracker.trackEvent(m,t.eventLogger),t.eventLogger&&t.eventLogger.writeEvent(m),p.debug("CloudFormation","pollEvents delivering event",{stackName:e,logicalId:m.logicalId,status:m.status,resourceType:m.resourceType}),c(m)}r=a?void 0:o.NextToken}while(r);return i&&t.activeNestedStacks.size>0?!1:i}catch(n){const r=k(n),i=S(n);return r==="ValidationError"&&i.includes(g)?!1:r==="Throttling"||r==="TooManyRequestsException"||r==="ThrottlingException"?"throttled":(i.includes(g)||p.error("CloudFormation","Unexpected polling error",{error:L(i)}),!1)}}E(j,"pollStackEvents");async function G(t,e,c,n,r,i){const a=await U(t,e),l=a?.status||"UNKNOWN",o=l.endsWith("_COMPLETE")&&!l.includes("ROLLBACK")&&!l.includes("FAILED");let s=a?.statusReason;!o&&c.size>0&&(s=(Array.from(c.values())[0]??s)||s);let u=null;if(!o&&r){const d=r.analyseFailure(i,e);u=d===null?null:M(d)}return n&&(n.writeDeploymentEnd(o,l,s),!o&&c.size>0&&n.writeFailureSummary(Array.from(c.entries()).map(([d,m])=>({logicalId:d,reason:m}))),u&&n.writeFailureAnalysis({rootCause:u.rootCause.reason,failedResources:u.affectedResources.map(d=>({logicalId:d.logicalId,resourceType:d.resourceType,reason:d.statusReason||"Unknown reason"})),dependencyChain:u.dependencyChain,remediation:u.remediation})),{success:o,failureMessage:s,analysis:u}}E(G,"handleStackCompletion");async function U(t,e,c){try{const n=new _({StackName:e}),a=(await t.getClient(R).send(n,{abortSignal:C(c)})).Stacks?.[0];return a?{status:a.StackStatus||"UNKNOWN",statusReason:a.StackStatusReason}:null}catch(n){return p.debug("CloudFormation","getStackStatus failed",{error:S(n)}),null}}E(U,"fetchStackStatus");async function Y(t,e,c){const n=[];try{const r=new A({StackName:e}),a=await t.getClient(R).send(r,{abortSignal:C(c)});if(!a.StackEvents)return n;const l=new Map;for(const o of a.StackEvents){const s=o.LogicalResourceId||"";if(s===e&&o.ResourceType===I)continue;const u=l.get(s);(!u||o.Timestamp&&u.timestamp<o.Timestamp)&&l.set(s,{logicalId:s,physicalId:o.PhysicalResourceId,resourceType:o.ResourceType||"",status:o.ResourceStatus||"",statusReason:o.ResourceStatusReason,timestamp:o.Timestamp||new Date,clientRequestToken:o.ClientRequestToken})}return Array.from(l.values())}catch(r){const i=k(r),a=S(r);return i==="ValidationError"&&a.includes(g)||a.includes(g)||p.error("CloudFormation","Error getting current resources",{error:L(S(r))}),n}}E(Y,"fetchCurrentResources");export{V as EVENT_CUTOFF_SKEW_MS,q as IpamConcurrencyTracker,Y as fetchCurrentResources,U as fetchStackStatus,G as handleStackCompletion,N as isIpamResource,W as isSuccessState,D as isTerminalState,j as pollStackEvents};
@@ -1 +1 @@
1
- import{logger as c}from"@fjall/util/logger";import{getErrorMessage as v,sleep as m}from"@fjall/util";import{STACK_NOT_FOUND_PATTERN as S}from"@fjall/util/aws";import{isResourceEvent as K,STACK_NOT_FOUND_PATTERN as O,CDK_NO_STACKS_MATCH as b}from"@fjall/util/aws";import{CF_STACK_RESOURCE_TYPE as p}from"./cloudformationEventTypes.js";import{isTerminalState as d,isSuccessState as M,IpamConcurrencyTracker as C,pollStackEvents as w,handleStackCompletion as E,fetchStackStatus as F,fetchCurrentResources as L,EVENT_CUTOFF_SKEW_MS as T}from"./cloudformationEventHelpers.js";class D{aws;seenEventIds=new Set;isMonitoring=!1;pollInterval=null;activeNestedStacks=new Map;failureReasons=new Map;eventHistory=new Map;eventLogger=null;failureAnalyser;eventLogWriterFactory;onFailureAnalysis;lastAnalysis=null;maxHistorySize=1e3;maxSeenEventIds=1e4;ipamTracker=new C;eventCutoffMs=0;tokenLatchCutoffMs=0;constructor(t,e){this.aws=t,this.failureAnalyser=e?.failureAnalyser??null,this.eventLogWriterFactory=e?.eventLogWriterFactory,this.onFailureAnalysis=e?.onFailureAnalysis}enableLogging(t,e,r,a){this.eventLogWriterFactory&&(this.eventLogger=this.eventLogWriterFactory(t,e,r,a))}async startMonitoring(t,e,r){if(this.isMonitoring){c.debug("CloudFormation","startMonitoring SKIPPED - already monitoring",{stackName:t});return}c.debug("CloudFormation","startMonitoring STARTED",{stackName:t}),this.isMonitoring=!0,this.seenEventIds.clear(),this.eventHistory.clear();const a=Date.now();this.eventCutoffMs=a-T,this.tokenLatchCutoffMs=a;try{await this.pollEvents(t,()=>{})}catch(o){const h=v(o);h.includes(S)||c.debug("CloudFormation","Initial poll failed",{error:h})}let s=5e3;const n=1e4;let g=0,l=0;const u=async()=>{if(this.isMonitoring)try{const o=await this.pollEvents(t,e);g++,o==="throttled"?(l++,s=Math.min(3e4,5e3*Math.pow(2,l-1))):(l=0,g>20&&s<n?s=Math.min(n,s+1e3):g<=20&&(s=5e3)),o===!0?await this.handleStackComplete(t,r):this.pollInterval=setTimeout(u,s)}catch(o){c.debug("CloudFormation","Polling iteration error (continuing)",{error:v(o)}),this.pollInterval=setTimeout(u,s)}};this.pollInterval=setTimeout(u,s)}stopMonitoring(){c.debug("CloudFormation","stopMonitoring called",{wasMonitoring:this.isMonitoring,seenEventCount:this.seenEventIds.size}),this.isMonitoring=!1,this.pollInterval&&(clearTimeout(this.pollInterval),this.pollInterval=null),this.cleanup()}cleanup(){if(this.activeNestedStacks.clear(),this.failureReasons.clear(),this.eventHistory.clear(),this.ipamTracker.clear(),this.seenEventIds.size>this.maxSeenEventIds){const t=Array.from(this.seenEventIds),e=Math.floor(this.maxSeenEventIds/2);this.seenEventIds=new Set(t.slice(-e))}this.eventLogger&&(this.eventLogger=null)}async handleStackComplete(t,e){const r=new Map(this.failureReasons),a=new Map(this.eventHistory),s=this.eventLogger;this.stopMonitoring();const n=await E(this.aws,t,r,s,this.failureAnalyser,a);this.lastAnalysis=n.analysis,n.analysis&&this.onFailureAnalysis&&this.onFailureAnalysis(n.analysis),e&&e(n.success,n.failureMessage)}getResourceHistory(t){return this.eventHistory.get(t)||[]}getEventHistory(){return new Map(this.eventHistory)}getFailureAnalysis(){return this.lastAnalysis}getFirstFailureReason(){return this.failureReasons.size>0?Array.from(this.failureReasons.values())[0]??null:null}getEventLogger(){return this.eventLogger}getEventLogPath(){return this.eventLogger?.getLogPath()||null}getLogSummary(){return this.eventLogger?.getLogSummary()||null}async waitForStackComplete(t,e={}){const{timeout:r=1800*1e3,pollInterval:a=2e3,onResourceUpdate:s,onStackComplete:n}=e,g=Date.now();let l,u=!1,o=!1,h;c.debug("CloudFormation","waitForStackComplete called",{stackName:t,timeout:r,pollInterval:a,hasOnResourceUpdate:!!s}),await this.startMonitoring(t,i=>{s&&s(i),i.resourceType===p&&i.logicalId===t&&(l=i.status,d(i.status)&&(u=!0,o=M(i.status),o||(h=this.getFirstFailureReason()||i.statusReason||"Stack operation failed")))},(i,f)=>{n&&n(i,f)});try{let i=!1,f=!1;for(;!u&&this.isMonitoring&&Date.now()-g<r;)await m(a),!i&&Date.now()-g>3e4&&!f&&(f=!0,await this.getStackStatus(t)||c.debug("CloudFormation","Stack not found after 30s, continuing to wait (CDK may be uploading assets)",{stackName:t})),l&&(i=!0);if(this.stopMonitoring(),!u){if(h)return{success:!1,status:"FAILED",failureReason:h,logPath:this.getLogSummary()||void 0};const y=await this.getStackStatus(t);return{success:!1,status:y?.status||"UNKNOWN",failureReason:`Deployment timed out after ${r/1e3} seconds. Stack status: ${y?.status||"UNKNOWN"}`,logPath:this.getLogSummary()||void 0}}return{success:o,status:l,failureReason:h,logPath:this.getLogSummary()||void 0}}catch(i){return this.stopMonitoring(),{success:!1,failureReason:`Monitoring error: ${v(i)}`,logPath:this.getLogSummary()||void 0}}}pollContext(){return{aws:this.aws,seenEventIds:this.seenEventIds,activeNestedStacks:this.activeNestedStacks,failureReasons:this.failureReasons,eventHistory:this.eventHistory,maxHistorySize:this.maxHistorySize,ipamTracker:this.ipamTracker,eventLogger:this.eventLogger,eventCutoffMs:this.eventCutoffMs,tokenLatchCutoffMs:this.tokenLatchCutoffMs}}async pollEvents(t,e){return w(this.pollContext(),t,e)}async getStackStatus(t){return F(this.aws,t)}async getCurrentResources(t){return L(this.aws,t)}}export{b as CDK_NO_STACKS_MATCH,D as CloudFormationEventMonitor,O as STACK_NOT_FOUND_PATTERN,K as isResourceEvent};
1
+ var p=Object.defineProperty;var v=(S,t)=>p(S,"name",{value:t,configurable:!0});import{logger as c}from"@fjall/util/logger";import{getErrorMessage as m,sleep as d}from"@fjall/util";import{STACK_NOT_FOUND_PATTERN as M}from"@fjall/util/aws";import{isResourceEvent as N,STACK_NOT_FOUND_PATTERN as W,CDK_NO_STACKS_MATCH as U}from"@fjall/util/aws";import{CF_STACK_RESOURCE_TYPE as C}from"./cloudformationEventTypes.js";import{isTerminalState as w,isSuccessState as E,IpamConcurrencyTracker as F,pollStackEvents as L,handleStackCompletion as T,fetchStackStatus as R,fetchCurrentResources as A,EVENT_CUTOFF_SKEW_MS as I}from"./cloudformationEventHelpers.js";class K{static{v(this,"CloudFormationEventMonitor")}aws;seenEventIds=new Set;isMonitoring=!1;pollInterval=null;activeNestedStacks=new Map;failureReasons=new Map;eventHistory=new Map;eventLogger=null;failureAnalyser;eventLogWriterFactory;onFailureAnalysis;lastAnalysis=null;maxHistorySize=1e3;maxSeenEventIds=1e4;ipamTracker=new F;eventCutoffMs=0;tokenLatchCutoffMs=0;constructor(t,e){this.aws=t,this.failureAnalyser=e?.failureAnalyser??null,this.eventLogWriterFactory=e?.eventLogWriterFactory,this.onFailureAnalysis=e?.onFailureAnalysis}enableLogging(t,e,r,a){this.eventLogWriterFactory&&(this.eventLogger=this.eventLogWriterFactory(t,e,r,a))}async startMonitoring(t,e,r){if(this.isMonitoring){c.debug("CloudFormation","startMonitoring SKIPPED - already monitoring",{stackName:t});return}c.debug("CloudFormation","startMonitoring STARTED",{stackName:t}),this.isMonitoring=!0,this.seenEventIds.clear(),this.eventHistory.clear();const a=Date.now();this.eventCutoffMs=a-I,this.tokenLatchCutoffMs=a;try{await this.pollEvents(t,()=>{})}catch(o){const h=m(o);h.includes(M)||c.debug("CloudFormation","Initial poll failed",{error:h})}let s=5e3;const n=1e4;let g=0,l=0;const u=v(async()=>{if(this.isMonitoring)try{const o=await this.pollEvents(t,e);g++,o==="throttled"?(l++,s=Math.min(3e4,5e3*Math.pow(2,l-1))):(l=0,g>20&&s<n?s=Math.min(n,s+1e3):g<=20&&(s=5e3)),o===!0?await this.handleStackComplete(t,r):this.pollInterval=setTimeout(u,s)}catch(o){c.debug("CloudFormation","Polling iteration error (continuing)",{error:m(o)}),this.pollInterval=setTimeout(u,s)}},"poll");this.pollInterval=setTimeout(u,s)}stopMonitoring(){c.debug("CloudFormation","stopMonitoring called",{wasMonitoring:this.isMonitoring,seenEventCount:this.seenEventIds.size}),this.isMonitoring=!1,this.pollInterval&&(clearTimeout(this.pollInterval),this.pollInterval=null),this.cleanup()}cleanup(){if(this.activeNestedStacks.clear(),this.failureReasons.clear(),this.eventHistory.clear(),this.ipamTracker.clear(),this.seenEventIds.size>this.maxSeenEventIds){const t=Array.from(this.seenEventIds),e=Math.floor(this.maxSeenEventIds/2);this.seenEventIds=new Set(t.slice(-e))}this.eventLogger&&(this.eventLogger=null)}async handleStackComplete(t,e){const r=new Map(this.failureReasons),a=new Map(this.eventHistory),s=this.eventLogger;this.stopMonitoring();const n=await T(this.aws,t,r,s,this.failureAnalyser,a);this.lastAnalysis=n.analysis,n.analysis&&this.onFailureAnalysis&&this.onFailureAnalysis(n.analysis),e&&e(n.success,n.failureMessage)}getResourceHistory(t){return this.eventHistory.get(t)||[]}getEventHistory(){return new Map(this.eventHistory)}getFailureAnalysis(){return this.lastAnalysis}getFirstFailureReason(){return this.failureReasons.size>0?Array.from(this.failureReasons.values())[0]??null:null}getEventLogger(){return this.eventLogger}getEventLogPath(){return this.eventLogger?.getLogPath()||null}getLogSummary(){return this.eventLogger?.getLogSummary()||null}async waitForStackComplete(t,e={}){const{timeout:r=1800*1e3,pollInterval:a=2e3,onResourceUpdate:s,onStackComplete:n}=e,g=Date.now();let l,u=!1,o=!1,h;c.debug("CloudFormation","waitForStackComplete called",{stackName:t,timeout:r,pollInterval:a,hasOnResourceUpdate:!!s}),await this.startMonitoring(t,i=>{s&&s(i),i.resourceType===C&&i.logicalId===t&&(l=i.status,w(i.status)&&(u=!0,o=E(i.status),o||(h=this.getFirstFailureReason()||i.statusReason||"Stack operation failed")))},(i,f)=>{n&&n(i,f)});try{let i=!1,f=!1;for(;!u&&this.isMonitoring&&Date.now()-g<r;)await d(a),!i&&Date.now()-g>3e4&&!f&&(f=!0,await this.getStackStatus(t)||c.debug("CloudFormation","Stack not found after 30s, continuing to wait (CDK may be uploading assets)",{stackName:t})),l&&(i=!0);if(this.stopMonitoring(),!u){if(h)return{success:!1,status:"FAILED",failureReason:h,logPath:this.getLogSummary()||void 0};const y=await this.getStackStatus(t);return{success:!1,status:y?.status||"UNKNOWN",failureReason:`Deployment timed out after ${r/1e3} seconds. Stack status: ${y?.status||"UNKNOWN"}`,logPath:this.getLogSummary()||void 0}}return{success:o,status:l,failureReason:h,logPath:this.getLogSummary()||void 0}}catch(i){return this.stopMonitoring(),{success:!1,failureReason:`Monitoring error: ${m(i)}`,logPath:this.getLogSummary()||void 0}}}pollContext(){return{aws:this.aws,seenEventIds:this.seenEventIds,activeNestedStacks:this.activeNestedStacks,failureReasons:this.failureReasons,eventHistory:this.eventHistory,maxHistorySize:this.maxHistorySize,ipamTracker:this.ipamTracker,eventLogger:this.eventLogger,eventCutoffMs:this.eventCutoffMs,tokenLatchCutoffMs:this.tokenLatchCutoffMs}}async pollEvents(t,e){return L(this.pollContext(),t,e)}async getStackStatus(t){return R(this.aws,t)}async getCurrentResources(t){return A(this.aws,t)}}export{U as CDK_NO_STACKS_MATCH,K as CloudFormationEventMonitor,W as STACK_NOT_FOUND_PATTERN,N as isResourceEvent};
@@ -62,8 +62,8 @@ export { reconcileProviderAccounts, mergeReconciledProviderAccounts } from "./or
62
62
  export type { ReconcileResult } from "./orchestration/index.js";
63
63
  export { decideNextTransition, reconcileTrailMigration, decommissionMemberTrailStorage, ORG_TRAIL_BUCKET_OUTPUT_KEY, TRAIL_BUCKET_OUTPUT_KEY, TRAIL_KEY_ARN_OUTPUT_KEY } from "./orchestration/index.js";
64
64
  export type { MemberTrailFacts, TrailMigrationTransition, TrailMigrationOutcome, DecommissionClients, DecommissionInput, DecommissionOutcome } from "./orchestration/index.js";
65
- export { classifyDriftFailure, fetchLastOperationEvents, DriftProbe, clearDriftSuspects, defaultDriftJournalDir, readDriftSuspects, recordDriftSuspects, detectStackDrift, runDriftPreFlight, formatDriftPreFlightBlock, DRIFT_PREFLIGHT_TRIGGER_STATUSES, DRIFT_PREFLIGHT_BUDGET_MS } from "./orchestration/index.js";
66
- export type { DriftProbeOptions, DriftProbeRunner, ProbeDriftSuspectsParams, DriftJournalOptions, RecordDriftSuspectsParams, DetectStackDriftDeps, DetectStackDriftParams, StackDriftReport, RunDriftPreFlightParams, DriftPreFlightBlockExtras, DriftPreFlightOutcome } from "./orchestration/index.js";
65
+ export { classifyDriftFailure, fetchLastOperationEvents, DriftProbe, clearDriftSuspects, defaultDriftJournalDir, readDriftSuspects, recordDriftSuspects, detectStackDrift, runDriftPreFlight, formatDriftPreFlightBlock, DRIFT_PREFLIGHT_TRIGGER_STATUSES, DRIFT_PREFLIGHT_BUDGET_MS, runRoute53RecordPreflight } from "./orchestration/index.js";
66
+ export type { DriftProbeOptions, DriftProbeRunner, ProbeDriftSuspectsParams, DriftJournalOptions, RecordDriftSuspectsParams, DetectStackDriftDeps, DetectStackDriftParams, StackDriftReport, RunDriftPreFlightParams, DriftPreFlightBlockExtras, DriftPreFlightOutcome, Route53RecordPreflightParams, Route53RecordPreflightOutcome, Route53RecordCollision } from "./orchestration/index.js";
67
67
  export { archiveCompletedRemediationJournal, computeRemediationOpId, defaultRemediationJournalDir, findRemediationJournalByOpId, listActiveRemediationOps, listActiveRemediationOpsForContext, readRemediationJournal, sweepExpiredRemediationJournals, writeRemediationJournal, REMEDIATION_JOURNAL_RETENTION_DAYS, captureRemediationForensics, defaultRemediationForensicsDir, applyRetainFlip, composeFlipCapabilities, computeTemplateDigest, deriveRetainFlipTemplate, findMarkedResourcesByOpId, proveFlipMetadataOnly, readRetainFlipState, verifyRetainFlipDiff, FORGET_MARKER_KEY, FORGET_FLIP_SPEC, RECREATE_MARKER_KEY, assessForgetResumability, completeForgetAfterConverge, completeForgetAfterDeploy, formatRecreateInFlightCures, partitionPreFlightByRemediation, runForgetSurgery, synthTemplateDeclaresResource, runRecreateSurgery, snapshotPolicyForType, SNAPSHOT_CAPABLE_RESOURCE_TYPES, runRecreatePreFlight, deriveRemovalTemplate, findRemovalDeletionFailures, findResourcesPresent, proveRemovalTargetsOnly, advertisableDriftVerbs, buildDriftRepairTicket, runDriftRepairGate, formatPinOutcomeDetail, pinProbeCoverage, renderPinReportLines, runPinRemediation, composePostFailureRepairOffer, formatDriftRepairOffer, DriftRepairAvailableError, checkDeployPathStackAvailability, isDeployPathBlockingStatus, StackUnavailableError, assertLeaseBeforeStack, LeaseDeniedError } from "./orchestration/index.js";
68
68
  export type { RemediationJournalContext, RemediationJournalOptions, CaptureRemediationForensicsParams, ForensicsCaptureOptions, RemediationForensicsBundle, ApplyRetainFlipOutcome, ApplyRetainFlipParams, DeriveRetainFlipParams, FlipDeletionPolicy, FlipProofResult, FlipSpec, RetainFlipDerivation, RetainFlipState, RecreateOrchestrationOptions, RecreateSurgeryParams, RecreateSurgeryReport, RecreatePreFlightStack, RunRecreatePreFlightParams, CoRemovedResource, ProveRemovalTargetsOnlyParams, RemovalDeletionFailure, RemovalDerivation, RemovalProofResult, StrippedExport, CompleteForgetAfterDeployParams, CompleteForgetOutcome, CompleteForgetParams, ForeignMarkerOperation, ForgetOrchestrationOptions, ForgetResumeAssessment, ForgetResumeSource, ForgetSurgeryParams, ForgetSurgeryReport, PreFlightRemediationDeps, PreFlightRemediationPartition, DriftRepairGateOutcome, RunDriftRepairGateParams, ComposePostFailureRepairOfferParams, PinnedProperty, PinProbeCoverage, PinRemediationReport, PinReportOnlyReason, PinResourceOutcome, PinTarget, RunPinRemediationParams } from "./orchestration/index.js";
69
69
  export { unlockBucket, unlockQueue } from "./orchestration/index.js";
@@ -79,6 +79,8 @@ export type { AccountsConfig } from "./orchestration/index.js";
79
79
  export { runOpenNextBuild } from "./orchestration/index.js";
80
80
  export { runOrganisationSetup, ORG_SETUP_PHASES } from "./orchestration/index.js";
81
81
  export type { OrgSetupPhase, OrgSetupCallbacks, OrgSetupConfig, OrgSetupResult, DockerProvider, DockerProgressCallback, DockerBuildParams, DockerBuildResult, ResolvedBuildSecret, ResolvedBuildSecrets, ECRInitParams, ECRInitResult, TagImagesParams, TagImagesResult, TagByDigestParams, DomainDeployProvider, DomainConfig, DomainDeployResult, DeployServices } from "./orchestration/index.js";
82
+ export { DOMAIN_DELEGATION_ERROR_TYPES, DomainDelegationError, DEFAULT_PUBLIC_RESOLVER_ADDRESSES, PUBLIC_DNS_QUERY_TIMEOUT_MS, createPublicResolvers, NS_PROPAGATION_DEFAULT_MAX_ATTEMPTS, NS_PROPAGATION_DEFAULT_INTERVAL_MS, waitForNsPropagation, ACM_CAA_ISSUERS, runCaaPreflight, deployDelegatedDomain, CROSS_ACCOUNT_DELEGATION_RESOURCE_TYPE, templateOwnsDelegationRecord, deleteChildNsFromParent, nsDelegationPhysicalName, runDelegatedDomainDestroy } from "./orchestration/index.js";
83
+ export type { DomainDeployPhase, DomainDelegationErrorType, PublicCaaRecord, PublicDnsResolver, NsPropagationGateParams, NsPropagationVerdict, CaaPreflightParams, CaaPreflightVerdict, DelegatedDomainDeployer, DelegatedZonePhaseState, DeployDelegatedDomainParams, DelegatedDomainDeployOutcome, NsDeleteOutcome, DeleteChildNsParams, DelegationDestroyNsOutcome, DelegatedDomainDestroyOutcome, DelegatedDomainDestroyParams } from "./orchestration/index.js";
82
84
  export { resolveBuildSecrets, resolveSecretRefValue, resolveBuildSecretSessionProvider, sourcedRefsFromBuildArgs, resolveBuildArgs, buildBuildSecretSessionPolicy, buildDeploySessionPolicy, partitionForRegion, validateBuildGroup } from "./orchestration/index.js";
83
85
  export type { ResolveBuildArgsContext, ResolveBuildArgsResult, SourcedBuildArgResolver, BuildSecretRef, BuildSecretSessionContext, DeploySessionContext, ValidateBuildGroupInput, BuildGroupMemberInputs } from "./orchestration/index.js";
84
86
  export type { FrameworkBuilder, FrameworkDetection, BuildPlan, BuildCommand, BuildCallbacks, DetectionContext, BuildOptions } from "./types/index.js";