@fjall/components-infrastructure 34.0.0 → 35.0.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.
- package/dist/lib/app.js +2 -0
- package/dist/lib/config/aws/deployBoundarySingleton.d.ts +6 -0
- package/dist/lib/config/aws/deployBoundarySingleton.js +10 -2
- package/dist/lib/config/aws/identityCenter.js +2 -0
- package/dist/lib/resources/aws/compute/lambda.d.ts +5 -0
- package/dist/lib/resources/aws/compute/lambda.js +8 -0
- package/dist/lib/resources/aws/iam/identityCenter/user.js +2 -0
- package/dist/lib/resources/aws/networking/vpcPeeringAccepterRole.js +3 -2
- package/dist/lib/resources/aws/networking/vpcPeeringConnection.js +2 -1
- package/dist/lib/resources/aws/storage/bucketDeployment.d.ts +5 -0
- package/dist/lib/resources/aws/storage/bucketDeployment.js +7 -0
- package/dist/lib/resources/aws/utilities/awsCustomResource.d.ts +9 -0
- package/dist/lib/resources/aws/utilities/awsCustomResource.js +16 -0
- package/dist/lib/resources/aws/utilities/customResource.js +2 -0
- package/dist/lib/resources/aws/utilities/customResourceProvider.d.ts +6 -0
- package/dist/lib/resources/aws/utilities/customResourceProvider.js +8 -0
- package/dist/lib/utils/deployTimeWriterMarkerAspect.d.ts +23 -0
- package/dist/lib/utils/deployTimeWriterMarkerAspect.js +35 -0
- package/dist/lib/utils/resourceTypeTags.d.ts +41 -0
- package/dist/lib/utils/resourceTypeTags.js +54 -0
- package/package.json +3 -3
package/dist/lib/app.js
CHANGED
|
@@ -12,6 +12,7 @@ import { ServiceDiscoveryNamespace } from "./resources/aws/networking/serviceDis
|
|
|
12
12
|
import { BuildkiteFactory } from "./patterns/aws/buildkite.js";
|
|
13
13
|
import { DevSubstrate } from "./patterns/aws/devSubstrate.js";
|
|
14
14
|
import { StandardTagsAspect } from "./utils/standardTagsAspect.js";
|
|
15
|
+
import { DeployTimeWriterMarkerAspect } from "./utils/deployTimeWriterMarkerAspect.js";
|
|
15
16
|
import { emitAppLevelAlbAliasExports } from "./utils/albAliasTargetRegistry.js";
|
|
16
17
|
import { emitAppLevelCdnAliasExports } from "./utils/cdnAliasTargetRegistry.js";
|
|
17
18
|
import { ensureFunctionLogGroups } from "./resources/aws/logging/logGroupHygiene.js";
|
|
@@ -61,6 +62,7 @@ export class App extends CdkApp {
|
|
|
61
62
|
context: { [RDS_PREVENT_RENDERING_DEPRECATED_CREDENTIALS]: true }
|
|
62
63
|
});
|
|
63
64
|
App.instance = this;
|
|
65
|
+
Aspects.of(this).add(new DeployTimeWriterMarkerAspect());
|
|
64
66
|
this.name = name ?? "FjallApp";
|
|
65
67
|
this.stackPrefix = toPascalCase(this.name);
|
|
66
68
|
// Initialise manifest collector for build-time service discovery
|
|
@@ -102,6 +102,12 @@ export declare const SECURITY_TIER_DIGESTS: {
|
|
|
102
102
|
* Five is IAM's per-policy version ceiling: at five, the oldest non-default
|
|
103
103
|
* version is dropped before the new default is published.
|
|
104
104
|
* `GetPolicyVersion` returns the document URL-encoded, hence the decode.
|
|
105
|
+
*
|
|
106
|
+
* TODO 2026-09-05, owner: paul — tag on adopt (a `TagPolicy` after the live
|
|
107
|
+
* read when the marker is absent, so an ADOPTED policy carries the marker the
|
|
108
|
+
* create path applies via `Tags: props.PolicyTags`); blocked on the
|
|
109
|
+
* inline-code budget, tracked in the deploy-boundary raises decisions design
|
|
110
|
+
* note (2026-09-05, § 6 "Train 8").
|
|
105
111
|
*/
|
|
106
112
|
export declare const DEPLOY_BOUNDARY_HANDLER_CORE: string;
|
|
107
113
|
/**
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { SECURITY_TIER_TO_BOUNDARY, deployBoundaryName } from "@fjall/generator";
|
|
1
|
+
import { SECURITY_TIER_TO_BOUNDARY, deployBoundaryName, deployBoundaryPolicyArn } from "@fjall/generator";
|
|
2
2
|
import { Aws, Duration } from "aws-cdk-lib";
|
|
3
3
|
import * as iam from "aws-cdk-lib/aws-iam";
|
|
4
4
|
import { Runtime } from "aws-cdk-lib/aws-lambda";
|
|
@@ -147,6 +147,12 @@ const ALLOWED_DIGEST_LITERALS = Object.values(SECURITY_TIER_DIGESTS)
|
|
|
147
147
|
* Five is IAM's per-policy version ceiling: at five, the oldest non-default
|
|
148
148
|
* version is dropped before the new default is published.
|
|
149
149
|
* `GetPolicyVersion` returns the document URL-encoded, hence the decode.
|
|
150
|
+
*
|
|
151
|
+
* TODO 2026-09-05, owner: paul — tag on adopt (a `TagPolicy` after the live
|
|
152
|
+
* read when the marker is absent, so an ADOPTED policy carries the marker the
|
|
153
|
+
* create path applies via `Tags: props.PolicyTags`); blocked on the
|
|
154
|
+
* inline-code budget, tracked in the deploy-boundary raises decisions design
|
|
155
|
+
* note (2026-09-05, § 6 "Train 8").
|
|
150
156
|
*/
|
|
151
157
|
export const DEPLOY_BOUNDARY_HANDLER_CORE = `const { IAMClient, GetPolicyCommand, GetPolicyVersionCommand, CreatePolicyCommand, CreatePolicyVersionCommand, ListPolicyVersionsCommand, DeletePolicyVersionCommand } = require('@aws-sdk/client-iam');
|
|
152
158
|
const { createHash } = require('crypto');
|
|
@@ -225,7 +231,9 @@ export class DeployBoundarySingleton extends Construct {
|
|
|
225
231
|
constructor(scope, id, props) {
|
|
226
232
|
super(scope, id);
|
|
227
233
|
this.policyName = deployBoundaryName(props.fjallOrgId);
|
|
228
|
-
|
|
234
|
+
// Same SSoT builder the seam-2 probe and the destroy sweep address the
|
|
235
|
+
// policy with — three renderers, one derivation.
|
|
236
|
+
const policyArn = deployBoundaryPolicyArn(Aws.PARTITION, Aws.ACCOUNT_ID, props.fjallOrgId);
|
|
229
237
|
const writer = new CustomResource(this, "Policy", {
|
|
230
238
|
runtime: Runtime.NODEJS_24_X,
|
|
231
239
|
timeout: Duration.seconds(DEPLOY_BOUNDARY_WRITER_TIMEOUT_SECONDS),
|
|
@@ -5,6 +5,7 @@ import { Annotations, CfnOutput, NestedStack } from "aws-cdk-lib";
|
|
|
5
5
|
import { AwsCustomResource } from "../../resources/aws/utilities/awsCustomResource.js";
|
|
6
6
|
import { Group, GroupMembership, PermissionSet, Assignment, IdentityStoreUser, IdentityStoreUserProvider, identityStoreUserArnPattern } from "../../resources/aws/iam/identityCenter/index.js";
|
|
7
7
|
import { ManagedPolicy, Role } from "../../resources/aws/iam/index.js";
|
|
8
|
+
import { markDeployTimeWriter } from "../../utils/resourceTypeTags.js";
|
|
8
9
|
import { allGroupNames, assignmentConstructId, boundGroupsForSet, effectivePermissionSets, implicitGroupNames, resolveTargetAccountNames, validateIdentityCentreConfig } from "./identityCentreConfig.js";
|
|
9
10
|
// CreateAccountAssignment has a 15-outstanding-async limit. Batching at 8
|
|
10
11
|
// keeps headroom for CFN's internal retries within the same backoff window.
|
|
@@ -74,6 +75,7 @@ export class IdentityCenter extends NestedStack {
|
|
|
74
75
|
})
|
|
75
76
|
}
|
|
76
77
|
});
|
|
78
|
+
markDeployTimeWriter(this.listInstancesRole);
|
|
77
79
|
}
|
|
78
80
|
listIdentityCenterInstance() {
|
|
79
81
|
const listInstancesCall = {
|
|
@@ -109,6 +109,11 @@ export interface LambdaFunctionProps {
|
|
|
109
109
|
/**
|
|
110
110
|
* Framework custom-resource handlers are tier-agnostic: the tier-owned
|
|
111
111
|
* knobs are excluded at the type rather than silently ignored.
|
|
112
|
+
*
|
|
113
|
+
* Every singleton in this library backs a custom-resource provider, which is
|
|
114
|
+
* why the constructor marks the hoisted subtree as a deploy-time writer. A
|
|
115
|
+
* singleton fronting a runtime lambda would need that call moved to the two
|
|
116
|
+
* writer call sites (`utilities/customResource.ts`, `iam/identityCenter/user.ts`).
|
|
112
117
|
*/
|
|
113
118
|
export interface SingletonFunctionProps extends Omit<LambdaFunctionProps, "tracing" | "appBackupTier"> {
|
|
114
119
|
uuid?: string;
|
|
@@ -18,6 +18,7 @@ import { resolveImportedSecret } from "../secrets/index.js";
|
|
|
18
18
|
import { validateSsmPathComponent } from "./ecsValidation.js";
|
|
19
19
|
import { toPascalCase } from "../../../utils/capitaliseString.js";
|
|
20
20
|
import { resolvePrivateSubnetType } from "../../../utils/vpcUtils.js";
|
|
21
|
+
import { markDeployTimeWriter } from "../../../utils/resourceTypeTags.js";
|
|
21
22
|
import { createLambdaAlarms } from "../monitoring/index.js";
|
|
22
23
|
function addPoliciesToRole(target, statements) {
|
|
23
24
|
for (const statement of statements) {
|
|
@@ -155,6 +156,13 @@ export class SingletonFunction extends singletonFunction {
|
|
|
155
156
|
addPoliciesToRole(this, props.inlinePolicy);
|
|
156
157
|
applyRoleDescription(this, props.roleDescription);
|
|
157
158
|
applyRolePathAndName(this, props.rolePath, props.roleName);
|
|
159
|
+
// aws-cdk-lib hoists the singleton's Function — and with it the execution
|
|
160
|
+
// role — to the stack scope, so a tag applied to this construct or to the
|
|
161
|
+
// enclosing writer never reaches either. Found the way CDK's own
|
|
162
|
+
// `ensureLambda` finds it, by the public `constructName`; `findChild`
|
|
163
|
+
// throws if the hoist ever moves, so a CDK upgrade fails at synth rather
|
|
164
|
+
// than shipping an unmarked role.
|
|
165
|
+
markDeployTimeWriter(Stack.of(this).node.findChild(this.constructName));
|
|
158
166
|
}
|
|
159
167
|
/**
|
|
160
168
|
* The Lambda's execution role (auto-generated by CDK)
|
|
@@ -7,6 +7,7 @@ import { Provider } from "aws-cdk-lib/custom-resources";
|
|
|
7
7
|
import { Construct } from "constructs";
|
|
8
8
|
import { SingletonFunction } from "../../compute/lambda.js";
|
|
9
9
|
import { DEFAULT_FRAMEWORK_LOG_RETENTION, LogGroup } from "../../logging/logGroup.js";
|
|
10
|
+
import { markDeployTimeWriter } from "../../../../utils/resourceTypeTags.js";
|
|
10
11
|
const __dirname = dirname(fileURLToPath(import.meta.url));
|
|
11
12
|
/**
|
|
12
13
|
* Resolves relative to the compiled location too — the build step copies the
|
|
@@ -58,6 +59,7 @@ export class IdentityStoreUserProvider extends Construct {
|
|
|
58
59
|
})
|
|
59
60
|
});
|
|
60
61
|
this.serviceToken = provider.serviceToken;
|
|
62
|
+
markDeployTimeWriter(this);
|
|
61
63
|
}
|
|
62
64
|
}
|
|
63
65
|
/**
|
|
@@ -1,7 +1,8 @@
|
|
|
1
1
|
import { Construct } from "constructs";
|
|
2
|
-
import { ArnFormat, Stack
|
|
2
|
+
import { ArnFormat, Stack } from "aws-cdk-lib";
|
|
3
3
|
import { AccountPrincipal, CompositePrincipal, PolicyDocument, PolicyStatement, Role } from "aws-cdk-lib/aws-iam";
|
|
4
4
|
import { applyCostAllocationTags } from "../../../utils/costAllocationTags.js";
|
|
5
|
+
import { applyResourceTypeTag, FJALL_RESOURCE_TYPES } from "../../../utils/resourceTypeTags.js";
|
|
5
6
|
export class VpcPeeringAccepterRole extends Construct {
|
|
6
7
|
role;
|
|
7
8
|
roleArn;
|
|
@@ -53,7 +54,7 @@ export class VpcPeeringAccepterRole extends Construct {
|
|
|
53
54
|
}
|
|
54
55
|
});
|
|
55
56
|
this.roleArn = this.role.roleArn;
|
|
56
|
-
|
|
57
|
+
applyResourceTypeTag(this, FJALL_RESOURCE_TYPES.vpcPeeringAccepter);
|
|
57
58
|
applyCostAllocationTags(this, {
|
|
58
59
|
service: "vpcPeeringAccepter",
|
|
59
60
|
domain: props.costAllocationDomain ?? props.localVpc.node.id,
|
|
@@ -4,6 +4,7 @@ import { CfnRoute, CfnVPCPeeringConnection, SubnetType } from "aws-cdk-lib/aws-e
|
|
|
4
4
|
import { PolicyStatement } from "aws-cdk-lib/aws-iam";
|
|
5
5
|
import { AwsCustomResourcePolicy, PhysicalResourceId } from "aws-cdk-lib/custom-resources";
|
|
6
6
|
import { applyCostAllocationTags } from "../../../utils/costAllocationTags.js";
|
|
7
|
+
import { applyResourceTypeTag, FJALL_RESOURCE_TYPES } from "../../../utils/resourceTypeTags.js";
|
|
7
8
|
import { AwsCustomResource } from "../utilities/awsCustomResource.js";
|
|
8
9
|
import { CrossAccountReturnRoutes } from "./crossAccountReturnRoutes.js";
|
|
9
10
|
export class VpcPeeringConnection extends Construct {
|
|
@@ -92,7 +93,7 @@ export class VpcPeeringConnection extends Construct {
|
|
|
92
93
|
enableDns: enableDnsResolution
|
|
93
94
|
});
|
|
94
95
|
}
|
|
95
|
-
|
|
96
|
+
applyResourceTypeTag(this, FJALL_RESOURCE_TYPES.vpcPeering);
|
|
96
97
|
Tags.of(this).add("fjall:peering:peer-vpc", props.peerVpcId);
|
|
97
98
|
if (props.peerAccountId) {
|
|
98
99
|
Tags.of(this).add("fjall:peering:peer-account", props.peerAccountId);
|
|
@@ -8,6 +8,11 @@ import { type Construct } from "constructs";
|
|
|
8
8
|
* service-auto-created. Per the Wrapper Routing Discipline rule (generator
|
|
9
9
|
* standards) and the infrastructure wrapper-routing pattern (§ A3), `lib/{config,
|
|
10
10
|
* patterns}/` MUST instantiate this class rather than the raw CDK construct.
|
|
11
|
+
*
|
|
12
|
+
* The handler is a deploy-time writer: aws-cdk-lib hoists its function and
|
|
13
|
+
* execution role to the stack scope under an id that embeds a per-variant
|
|
14
|
+
* uuid (memory, VPC, ephemeral storage), so the role is marked through the
|
|
15
|
+
* public `handlerRole` seam rather than by construct id.
|
|
11
16
|
*/
|
|
12
17
|
export declare class BucketDeployment extends CdkBucketDeployment {
|
|
13
18
|
constructor(scope: Construct, id: string, props: BucketDeploymentProps);
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { BucketDeployment as CdkBucketDeployment } from "aws-cdk-lib/aws-s3-deployment";
|
|
2
|
+
import { markDeployTimeWriter } from "../../../utils/resourceTypeTags.js";
|
|
2
3
|
import { stackSingletonLogGroup } from "../logging/singletonLogGroup.js";
|
|
3
4
|
/**
|
|
4
5
|
* Routing-point wrapper for `aws-cdk-lib/aws-s3-deployment.BucketDeployment`.
|
|
@@ -8,6 +9,11 @@ import { stackSingletonLogGroup } from "../logging/singletonLogGroup.js";
|
|
|
8
9
|
* service-auto-created. Per the Wrapper Routing Discipline rule (generator
|
|
9
10
|
* standards) and the infrastructure wrapper-routing pattern (§ A3), `lib/{config,
|
|
10
11
|
* patterns}/` MUST instantiate this class rather than the raw CDK construct.
|
|
12
|
+
*
|
|
13
|
+
* The handler is a deploy-time writer: aws-cdk-lib hoists its function and
|
|
14
|
+
* execution role to the stack scope under an id that embeds a per-variant
|
|
15
|
+
* uuid (memory, VPC, ephemeral storage), so the role is marked through the
|
|
16
|
+
* public `handlerRole` seam rather than by construct id.
|
|
11
17
|
*/
|
|
12
18
|
export class BucketDeployment extends CdkBucketDeployment {
|
|
13
19
|
constructor(scope, id, props) {
|
|
@@ -19,5 +25,6 @@ export class BucketDeployment extends CdkBucketDeployment {
|
|
|
19
25
|
// site instantiates first.
|
|
20
26
|
logGroup: props.logGroup ?? stackSingletonLogGroup(scope, "BucketDeployment")
|
|
21
27
|
});
|
|
28
|
+
markDeployTimeWriter(this.handlerRole);
|
|
22
29
|
}
|
|
23
30
|
}
|
|
@@ -1,5 +1,14 @@
|
|
|
1
1
|
import * as customResource from "aws-cdk-lib/custom-resources";
|
|
2
2
|
import { type Construct } from "constructs";
|
|
3
|
+
/**
|
|
4
|
+
* The stack-scope construct id under which aws-cdk-lib hoists the one
|
|
5
|
+
* `AwsCustomResource` provider every site in a stack shares: the singleton's
|
|
6
|
+
* `lambdaPurpose` ("AWS") followed by its uuid with the hyphens stripped —
|
|
7
|
+
* `SingletonFunction.constructName`'s derivation, from the public
|
|
8
|
+
* `PROVIDER_FUNCTION_UUID`. Test fixtures address the hoisted function and
|
|
9
|
+
* its execution role by this prefix.
|
|
10
|
+
*/
|
|
11
|
+
export declare const AWS_CUSTOM_RESOURCE_PROVIDER_ID: string;
|
|
3
12
|
export declare class AwsCustomResource extends customResource.AwsCustomResource {
|
|
4
13
|
readonly response: string;
|
|
5
14
|
constructor(scope: Construct, id: string, props: customResource.AwsCustomResourceProps);
|
|
@@ -1,5 +1,16 @@
|
|
|
1
|
+
import { Stack } from "aws-cdk-lib";
|
|
1
2
|
import * as customResource from "aws-cdk-lib/custom-resources";
|
|
3
|
+
import { markDeployTimeWriter } from "../../../utils/resourceTypeTags.js";
|
|
2
4
|
import { stackSingletonLogGroup } from "../logging/singletonLogGroup.js";
|
|
5
|
+
/**
|
|
6
|
+
* The stack-scope construct id under which aws-cdk-lib hoists the one
|
|
7
|
+
* `AwsCustomResource` provider every site in a stack shares: the singleton's
|
|
8
|
+
* `lambdaPurpose` ("AWS") followed by its uuid with the hyphens stripped —
|
|
9
|
+
* `SingletonFunction.constructName`'s derivation, from the public
|
|
10
|
+
* `PROVIDER_FUNCTION_UUID`. Test fixtures address the hoisted function and
|
|
11
|
+
* its execution role by this prefix.
|
|
12
|
+
*/
|
|
13
|
+
export const AWS_CUSTOM_RESOURCE_PROVIDER_ID = `AWS${customResource.AwsCustomResource.PROVIDER_FUNCTION_UUID.replace(/[^a-zA-Z0-9]/g, "")}`;
|
|
3
14
|
export class AwsCustomResource extends customResource.AwsCustomResource {
|
|
4
15
|
response;
|
|
5
16
|
constructor(scope, id, props) {
|
|
@@ -14,5 +25,10 @@ export class AwsCustomResource extends customResource.AwsCustomResource {
|
|
|
14
25
|
resources: customResource.AwsCustomResourcePolicy.ANY_RESOURCE
|
|
15
26
|
})
|
|
16
27
|
});
|
|
28
|
+
// aws-cdk-lib hoists the shared provider — and, unless the site passed
|
|
29
|
+
// `role`, its execution role — to the stack scope, out of reach of any
|
|
30
|
+
// tag on this construct. `findChild` throws if the hoist ever moves, so a
|
|
31
|
+
// CDK upgrade that would silently un-mark the role fails at synth.
|
|
32
|
+
markDeployTimeWriter(Stack.of(this).node.findChild(AWS_CUSTOM_RESOURCE_PROVIDER_ID));
|
|
17
33
|
}
|
|
18
34
|
}
|
|
@@ -5,6 +5,7 @@ import { Construct } from "constructs";
|
|
|
5
5
|
import { SingletonFunction, LambdaFunction } from "../compute/lambda.js";
|
|
6
6
|
import { DEFAULT_CUSTOM_RESOURCE_TIMEOUT_SECONDS } from "../compute/ecsConstants.js";
|
|
7
7
|
import { DEFAULT_FRAMEWORK_LOG_RETENTION, LogGroup } from "../logging/logGroup.js";
|
|
8
|
+
import { markDeployTimeWriter } from "../../../utils/resourceTypeTags.js";
|
|
8
9
|
export class CustomResource extends Construct {
|
|
9
10
|
response;
|
|
10
11
|
resource;
|
|
@@ -71,5 +72,6 @@ export class CustomResource extends Construct {
|
|
|
71
72
|
})
|
|
72
73
|
});
|
|
73
74
|
this.response = this.resource.getAtt("Response").toString();
|
|
75
|
+
markDeployTimeWriter(this);
|
|
74
76
|
}
|
|
75
77
|
}
|
|
@@ -1,5 +1,11 @@
|
|
|
1
1
|
import { Provider, type ProviderProps } from "aws-cdk-lib/custom-resources";
|
|
2
2
|
import { type Construct } from "constructs";
|
|
3
|
+
/**
|
|
4
|
+
* Routing-point wrapper for the custom-resource provider framework. The
|
|
5
|
+
* framework's onEvent/isComplete functions and their roles are deploy-time
|
|
6
|
+
* writers; the caller's own handler (`props.onEventHandler`) lives outside
|
|
7
|
+
* this scope and is the caller's to mark.
|
|
8
|
+
*/
|
|
3
9
|
export declare class CustomResourceProvider extends Provider {
|
|
4
10
|
constructor(scope: Construct, id: string, props: ProviderProps);
|
|
5
11
|
}
|
|
@@ -1,6 +1,14 @@
|
|
|
1
1
|
import { Provider } from "aws-cdk-lib/custom-resources";
|
|
2
|
+
import { markDeployTimeWriter } from "../../../utils/resourceTypeTags.js";
|
|
3
|
+
/**
|
|
4
|
+
* Routing-point wrapper for the custom-resource provider framework. The
|
|
5
|
+
* framework's onEvent/isComplete functions and their roles are deploy-time
|
|
6
|
+
* writers; the caller's own handler (`props.onEventHandler`) lives outside
|
|
7
|
+
* this scope and is the caller's to mark.
|
|
8
|
+
*/
|
|
2
9
|
export class CustomResourceProvider extends Provider {
|
|
3
10
|
constructor(scope, id, props) {
|
|
4
11
|
super(scope, id, props);
|
|
12
|
+
markDeployTimeWriter(this);
|
|
5
13
|
}
|
|
6
14
|
}
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
import { type IAspect } from "aws-cdk-lib";
|
|
2
|
+
import type { IConstruct } from "constructs";
|
|
3
|
+
/**
|
|
4
|
+
* Marks the execution roles aws-cdk-lib's lightweight custom-resource
|
|
5
|
+
* provider framework (`CustomResourceProviderBase`) renders as raw
|
|
6
|
+
* `AWS::IAM::Role` CfnResources: `Custom::VpcRestrictDefaultSG` under the
|
|
7
|
+
* `@aws-cdk/aws-ec2:restrictDefaultSecurityGroup` flag every scaffolded
|
|
8
|
+
* `cdk.json` sets, `Custom::CrossAccountZoneDelegation`, and whichever the
|
|
9
|
+
* next CDK release adds. Those roles have no tag manager, so `Tags.of()` —
|
|
10
|
+
* the wrappers' mechanism — never reaches them; this aspect writes the marker
|
|
11
|
+
* as a property override instead. Registered once per `App`, so every stack
|
|
12
|
+
* gets it without a per-construct site to forget.
|
|
13
|
+
*
|
|
14
|
+
* Recognised structurally, not by construct id: a `Role` CfnResource whose
|
|
15
|
+
* parent exposes the framework's `serviceToken` + `roleArn` pair. Prototype
|
|
16
|
+
* probing rather than `instanceof`, for the duplicate-aws-cdk-lib reason
|
|
17
|
+
* `logGroupHygiene` records. The full `Provider` framework exposes no
|
|
18
|
+
* `roleArn` and builds L2 roles, so it is not matched here — its wrappers tag
|
|
19
|
+
* it directly.
|
|
20
|
+
*/
|
|
21
|
+
export declare class DeployTimeWriterMarkerAspect implements IAspect {
|
|
22
|
+
visit(node: IConstruct): void;
|
|
23
|
+
}
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
import { CfnResource } from "aws-cdk-lib";
|
|
2
|
+
import { markDeployTimeWriterCfn } from "./resourceTypeTags.js";
|
|
3
|
+
/**
|
|
4
|
+
* Marks the execution roles aws-cdk-lib's lightweight custom-resource
|
|
5
|
+
* provider framework (`CustomResourceProviderBase`) renders as raw
|
|
6
|
+
* `AWS::IAM::Role` CfnResources: `Custom::VpcRestrictDefaultSG` under the
|
|
7
|
+
* `@aws-cdk/aws-ec2:restrictDefaultSecurityGroup` flag every scaffolded
|
|
8
|
+
* `cdk.json` sets, `Custom::CrossAccountZoneDelegation`, and whichever the
|
|
9
|
+
* next CDK release adds. Those roles have no tag manager, so `Tags.of()` —
|
|
10
|
+
* the wrappers' mechanism — never reaches them; this aspect writes the marker
|
|
11
|
+
* as a property override instead. Registered once per `App`, so every stack
|
|
12
|
+
* gets it without a per-construct site to forget.
|
|
13
|
+
*
|
|
14
|
+
* Recognised structurally, not by construct id: a `Role` CfnResource whose
|
|
15
|
+
* parent exposes the framework's `serviceToken` + `roleArn` pair. Prototype
|
|
16
|
+
* probing rather than `instanceof`, for the duplicate-aws-cdk-lib reason
|
|
17
|
+
* `logGroupHygiene` records. The full `Provider` framework exposes no
|
|
18
|
+
* `roleArn` and builds L2 roles, so it is not matched here — its wrappers tag
|
|
19
|
+
* it directly.
|
|
20
|
+
*/
|
|
21
|
+
export class DeployTimeWriterMarkerAspect {
|
|
22
|
+
visit(node) {
|
|
23
|
+
if (!CfnResource.isCfnResource(node) ||
|
|
24
|
+
node.cfnResourceType !== "AWS::IAM::Role") {
|
|
25
|
+
return;
|
|
26
|
+
}
|
|
27
|
+
const parent = node.node.scope;
|
|
28
|
+
if (parent === undefined || !isLightweightProvider(parent))
|
|
29
|
+
return;
|
|
30
|
+
markDeployTimeWriterCfn(node);
|
|
31
|
+
}
|
|
32
|
+
}
|
|
33
|
+
function isLightweightProvider(construct) {
|
|
34
|
+
return "serviceToken" in construct && "roleArn" in construct;
|
|
35
|
+
}
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
import { FJALL_RESOURCE_TYPE_TAG_KEY, FJALL_RESOURCE_TYPES, type FjallResourceType } from "@fjall/util/aws";
|
|
2
|
+
import { type CfnResource } from "aws-cdk-lib";
|
|
3
|
+
import type { IConstruct } from "constructs";
|
|
4
|
+
export { FJALL_RESOURCE_TYPE_TAG_KEY, FJALL_RESOURCE_TYPES, type FjallResourceType };
|
|
5
|
+
/**
|
|
6
|
+
* Stamp the `fjall:resource:type` marker on a construct subtree. The marker
|
|
7
|
+
* answers "what IS this resource" for resources whose physical name is
|
|
8
|
+
* CloudFormation-generated, so a consumer never has to pattern-match a name.
|
|
9
|
+
*/
|
|
10
|
+
export declare function applyResourceTypeTag(scope: IConstruct, resourceType: FjallResourceType): void;
|
|
11
|
+
/**
|
|
12
|
+
* Mark a deploy-time-only writer subtree — the execution role CloudFormation
|
|
13
|
+
* assumes while driving a custom resource, plus the handler, the provider
|
|
14
|
+
* framework function and their log groups.
|
|
15
|
+
*
|
|
16
|
+
* Every writer producer in the library routes through one of two mechanisms,
|
|
17
|
+
* nothing stamping the key by hand:
|
|
18
|
+
*
|
|
19
|
+
* - this helper, called by each wrapper that constructs a writer: the shared
|
|
20
|
+
* `CustomResource` utility and the `CustomResourceProvider` wrapper (their
|
|
21
|
+
* own subtree), the `SingletonFunction`, `AwsCustomResource` and
|
|
22
|
+
* `BucketDeployment` wrappers (the function and role aws-cdk-lib hoists to
|
|
23
|
+
* the stack scope, out of reach of any tag on the wrapper itself), the
|
|
24
|
+
* identity-store user provider, and the Identity Centre ListInstances role;
|
|
25
|
+
* - `DeployTimeWriterMarkerAspect` (registered by `App`), for the execution
|
|
26
|
+
* roles aws-cdk-lib's lightweight provider framework renders as raw
|
|
27
|
+
* CfnResources — `Tags.of()` cannot reach those, so the marker goes on as a
|
|
28
|
+
* property override via `markDeployTimeWriterCfn`.
|
|
29
|
+
*
|
|
30
|
+
* The producer set is enumerated from source by
|
|
31
|
+
* `__tests__/deployTimeWriterCensus.test.ts`: a new construction of a CDK
|
|
32
|
+
* writer class fails that test until it names the mechanism that marks it.
|
|
33
|
+
*/
|
|
34
|
+
export declare function markDeployTimeWriter(scope: IConstruct): void;
|
|
35
|
+
/**
|
|
36
|
+
* The raw-CfnResource twin of `markDeployTimeWriter`, for a role the
|
|
37
|
+
* lightweight `CustomResourceProviderBase` framework renders without a tag
|
|
38
|
+
* manager: the marker is written straight into the resource's `Tags`
|
|
39
|
+
* property. Called only by `DeployTimeWriterMarkerAspect`.
|
|
40
|
+
*/
|
|
41
|
+
export declare function markDeployTimeWriterCfn(role: CfnResource): void;
|
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
import { FJALL_RESOURCE_TYPE_TAG_KEY, FJALL_RESOURCE_TYPES } from "@fjall/util/aws";
|
|
2
|
+
import { Tags } from "aws-cdk-lib";
|
|
3
|
+
// Canonical home: @fjall/util (`util/src/aws/infraTags.ts`) — shared with the
|
|
4
|
+
// Quick-Create template's hand-written twin and the webapp posture exclusion.
|
|
5
|
+
// Re-exported here so construct-package consumers keep one import path.
|
|
6
|
+
export { FJALL_RESOURCE_TYPE_TAG_KEY, FJALL_RESOURCE_TYPES };
|
|
7
|
+
/**
|
|
8
|
+
* Stamp the `fjall:resource:type` marker on a construct subtree. The marker
|
|
9
|
+
* answers "what IS this resource" for resources whose physical name is
|
|
10
|
+
* CloudFormation-generated, so a consumer never has to pattern-match a name.
|
|
11
|
+
*/
|
|
12
|
+
export function applyResourceTypeTag(scope, resourceType) {
|
|
13
|
+
Tags.of(scope).add(FJALL_RESOURCE_TYPE_TAG_KEY, resourceType);
|
|
14
|
+
}
|
|
15
|
+
/**
|
|
16
|
+
* Mark a deploy-time-only writer subtree — the execution role CloudFormation
|
|
17
|
+
* assumes while driving a custom resource, plus the handler, the provider
|
|
18
|
+
* framework function and their log groups.
|
|
19
|
+
*
|
|
20
|
+
* Every writer producer in the library routes through one of two mechanisms,
|
|
21
|
+
* nothing stamping the key by hand:
|
|
22
|
+
*
|
|
23
|
+
* - this helper, called by each wrapper that constructs a writer: the shared
|
|
24
|
+
* `CustomResource` utility and the `CustomResourceProvider` wrapper (their
|
|
25
|
+
* own subtree), the `SingletonFunction`, `AwsCustomResource` and
|
|
26
|
+
* `BucketDeployment` wrappers (the function and role aws-cdk-lib hoists to
|
|
27
|
+
* the stack scope, out of reach of any tag on the wrapper itself), the
|
|
28
|
+
* identity-store user provider, and the Identity Centre ListInstances role;
|
|
29
|
+
* - `DeployTimeWriterMarkerAspect` (registered by `App`), for the execution
|
|
30
|
+
* roles aws-cdk-lib's lightweight provider framework renders as raw
|
|
31
|
+
* CfnResources — `Tags.of()` cannot reach those, so the marker goes on as a
|
|
32
|
+
* property override via `markDeployTimeWriterCfn`.
|
|
33
|
+
*
|
|
34
|
+
* The producer set is enumerated from source by
|
|
35
|
+
* `__tests__/deployTimeWriterCensus.test.ts`: a new construction of a CDK
|
|
36
|
+
* writer class fails that test until it names the mechanism that marks it.
|
|
37
|
+
*/
|
|
38
|
+
export function markDeployTimeWriter(scope) {
|
|
39
|
+
applyResourceTypeTag(scope, FJALL_RESOURCE_TYPES.deployTimeWriter);
|
|
40
|
+
}
|
|
41
|
+
/**
|
|
42
|
+
* The raw-CfnResource twin of `markDeployTimeWriter`, for a role the
|
|
43
|
+
* lightweight `CustomResourceProviderBase` framework renders without a tag
|
|
44
|
+
* manager: the marker is written straight into the resource's `Tags`
|
|
45
|
+
* property. Called only by `DeployTimeWriterMarkerAspect`.
|
|
46
|
+
*/
|
|
47
|
+
export function markDeployTimeWriterCfn(role) {
|
|
48
|
+
role.addPropertyOverride("Tags", [
|
|
49
|
+
{
|
|
50
|
+
Key: FJALL_RESOURCE_TYPE_TAG_KEY,
|
|
51
|
+
Value: FJALL_RESOURCE_TYPES.deployTimeWriter
|
|
52
|
+
}
|
|
53
|
+
]);
|
|
54
|
+
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@fjall/components-infrastructure",
|
|
3
|
-
"version": "
|
|
3
|
+
"version": "35.0.0",
|
|
4
4
|
"repository": {
|
|
5
5
|
"type": "git",
|
|
6
6
|
"url": "git+https://github.com/fjall-tech/fjall.git",
|
|
@@ -80,8 +80,8 @@
|
|
|
80
80
|
},
|
|
81
81
|
"dependencies": {
|
|
82
82
|
"@aws-sdk/client-organizations": "^3.1098.0",
|
|
83
|
-
"@fjall/generator": "^
|
|
84
|
-
"@fjall/util": "^
|
|
83
|
+
"@fjall/generator": "^35.0.0",
|
|
84
|
+
"@fjall/util": "^35.0.0",
|
|
85
85
|
"constructs": "^10.7.2",
|
|
86
86
|
"zod": "^4.4.3"
|
|
87
87
|
},
|