@fjall/components-infrastructure 0.96.0 → 0.99.1

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/lib/app.d.ts +68 -1
  2. package/dist/lib/app.js +113 -4
  3. package/dist/lib/config/aws/__t17fixture.d.ts +1 -0
  4. package/dist/lib/config/aws/__t17fixture.js +3 -0
  5. package/dist/lib/config/aws/__t17fixtureType.d.ts +2 -0
  6. package/dist/lib/config/aws/__t17fixtureType.js +1 -0
  7. package/dist/lib/config/aws/alarmTopic.js +8 -4
  8. package/dist/lib/config/aws/cloudTrail.js +1 -1
  9. package/dist/lib/config/aws/disasterRecovery.js +11 -16
  10. package/dist/lib/config/aws/ecrDefaultImage.d.ts +0 -1
  11. package/dist/lib/config/aws/ecrDefaultImage.js +13 -23
  12. package/dist/lib/config/aws/identityCenter.d.ts +10 -3
  13. package/dist/lib/config/aws/identityCenter.js +101 -37
  14. package/dist/lib/config/aws/identityCenterGroupMembership.js +8 -2
  15. package/dist/lib/config/aws/identityCenterMembership.d.ts +11 -0
  16. package/dist/lib/config/aws/identityCenterMembership.js +61 -0
  17. package/dist/lib/config/aws/index.d.ts +1 -1
  18. package/dist/lib/config/aws/index.js +1 -1
  19. package/dist/lib/config/aws/ipam.js +6 -11
  20. package/dist/lib/config/aws/oidcConnector.js +5 -1
  21. package/dist/lib/config/aws/scpPreset.js +4 -1
  22. package/dist/lib/patterns/aws/_eslint_test_tmp/leak.d.ts +1 -0
  23. package/dist/lib/patterns/aws/_eslint_test_tmp/leak.js +4 -0
  24. package/dist/lib/patterns/aws/account.js +2 -4
  25. package/dist/lib/patterns/aws/apexDomainPattern.js +10 -10
  26. package/dist/lib/patterns/aws/bastionFactory.d.ts +10 -0
  27. package/dist/lib/patterns/aws/bastionFactory.js +29 -0
  28. package/dist/lib/patterns/aws/buildkite.d.ts +2 -2
  29. package/dist/lib/patterns/aws/buildkite.js +51 -97
  30. package/dist/lib/patterns/aws/cdn.js +1 -1
  31. package/dist/lib/patterns/aws/clickhouseDatabase.d.ts +172 -0
  32. package/dist/lib/patterns/aws/clickhouseDatabase.js +600 -0
  33. package/dist/lib/patterns/aws/compute.d.ts +4 -6
  34. package/dist/lib/patterns/aws/compute.js +7 -13
  35. package/dist/lib/patterns/aws/computeEcs.d.ts +93 -5
  36. package/dist/lib/patterns/aws/computeEcs.js +867 -37
  37. package/dist/lib/patterns/aws/computeEcsTypes.d.ts +528 -25
  38. package/dist/lib/patterns/aws/computeEcsTypes.js +10 -0
  39. package/dist/lib/patterns/aws/computeLambda.d.ts +0 -5
  40. package/dist/lib/patterns/aws/computeLambda.js +1 -2
  41. package/dist/lib/patterns/aws/database.d.ts +50 -8
  42. package/dist/lib/patterns/aws/database.js +183 -27
  43. package/dist/lib/patterns/aws/domain.js +6 -4
  44. package/dist/lib/patterns/aws/index.d.ts +1 -0
  45. package/dist/lib/patterns/aws/index.js +1 -0
  46. package/dist/lib/patterns/aws/interfaces/compute.d.ts +7 -1
  47. package/dist/lib/patterns/aws/interfaces/database.d.ts +187 -8
  48. package/dist/lib/patterns/aws/interfaces/database.js +17 -3
  49. package/dist/lib/patterns/aws/interfaces/index.d.ts +2 -1
  50. package/dist/lib/patterns/aws/interfaces/index.js +3 -1
  51. package/dist/lib/patterns/aws/interfaces/messaging.d.ts +7 -0
  52. package/dist/lib/patterns/aws/interfaces/migrationContributor.d.ts +47 -0
  53. package/dist/lib/patterns/aws/interfaces/migrationContributor.js +9 -0
  54. package/dist/lib/patterns/aws/messaging.d.ts +66 -10
  55. package/dist/lib/patterns/aws/messaging.js +115 -20
  56. package/dist/lib/patterns/aws/network.js +16 -7
  57. package/dist/lib/patterns/aws/organisation.d.ts +4 -0
  58. package/dist/lib/patterns/aws/organisation.js +22 -4
  59. package/dist/lib/patterns/aws/storage.d.ts +1 -2
  60. package/dist/lib/patterns/aws/storage.js +3 -2
  61. package/dist/lib/patterns/aws/vpcPeer.js +3 -1
  62. package/dist/lib/resources/aws/analytics/clickhouse.js +18 -9
  63. package/dist/lib/resources/aws/analytics/clickhouseAlarms.d.ts +24 -9
  64. package/dist/lib/resources/aws/analytics/clickhouseAlarms.js +61 -10
  65. package/dist/lib/resources/aws/analytics/clickhouseConstants.d.ts +3 -3
  66. package/dist/lib/resources/aws/analytics/clickhouseConstants.js +3 -3
  67. package/dist/lib/resources/aws/analytics/clickhouseTypes.d.ts +7 -1
  68. package/dist/lib/resources/aws/analytics/clickhouseUserData.d.ts +1 -1
  69. package/dist/lib/resources/aws/analytics/clickhouseUserData.js +53 -3
  70. package/dist/lib/resources/aws/base/awsStack.js +4 -2
  71. package/dist/lib/resources/aws/compute/__tmp__/regression-shape.d.ts +2 -0
  72. package/dist/lib/resources/aws/compute/__tmp__/regression-shape.js +11 -0
  73. package/dist/lib/resources/aws/compute/asgInlineLifecycleHook.d.ts +52 -0
  74. package/dist/lib/resources/aws/compute/asgInlineLifecycleHook.js +60 -0
  75. package/dist/lib/resources/aws/compute/blockDeviceVolume.d.ts +8 -0
  76. package/dist/lib/resources/aws/compute/blockDeviceVolume.js +10 -0
  77. package/dist/lib/resources/aws/compute/ec2.d.ts +132 -12
  78. package/dist/lib/resources/aws/compute/ec2.js +163 -23
  79. package/dist/lib/resources/aws/compute/ec2GracefulTerminationHandler.d.ts +41 -0
  80. package/dist/lib/resources/aws/compute/ec2GracefulTerminationHandler.js +194 -0
  81. package/dist/lib/resources/aws/compute/ec2GracefulTerminationLambda.source.cjs +458 -0
  82. package/dist/lib/resources/aws/compute/ecs.d.ts +27 -1
  83. package/dist/lib/resources/aws/compute/ecs.js +42 -2
  84. package/dist/lib/resources/aws/compute/ecsConstants.d.ts +9 -0
  85. package/dist/lib/resources/aws/compute/ecsConstants.js +16 -0
  86. package/dist/lib/resources/aws/compute/ecsImages.js +32 -20
  87. package/dist/lib/resources/aws/compute/ecsLifecycleHookMigration.d.ts +96 -0
  88. package/dist/lib/resources/aws/compute/ecsLifecycleHookMigration.js +113 -0
  89. package/dist/lib/resources/aws/compute/ecsNetworking.d.ts +2 -1
  90. package/dist/lib/resources/aws/compute/ecsNetworking.js +18 -6
  91. package/dist/lib/resources/aws/compute/ecsServiceFactory.d.ts +13 -4
  92. package/dist/lib/resources/aws/compute/ecsServiceFactory.js +155 -33
  93. package/dist/lib/resources/aws/compute/ecsTaskDefinition.d.ts +31 -1
  94. package/dist/lib/resources/aws/compute/ecsTaskDefinition.js +102 -6
  95. package/dist/lib/resources/aws/compute/ecsTypes.d.ts +173 -13
  96. package/dist/lib/resources/aws/compute/ecsValidation.d.ts +9 -0
  97. package/dist/lib/resources/aws/compute/ecsValidation.js +63 -0
  98. package/dist/lib/resources/aws/compute/index.d.ts +2 -0
  99. package/dist/lib/resources/aws/compute/index.js +2 -0
  100. package/dist/lib/resources/aws/compute/lambda.d.ts +7 -13
  101. package/dist/lib/resources/aws/compute/lambda.js +30 -38
  102. package/dist/lib/resources/aws/compute/lifecycleHookLambda.source.cjs +192 -0
  103. package/dist/lib/resources/aws/compute/persistentDataVolume.d.ts +104 -0
  104. package/dist/lib/resources/aws/compute/persistentDataVolume.js +245 -0
  105. package/dist/lib/resources/aws/compute/persistentDataVolumeLambda.source.cjs +398 -0
  106. package/dist/lib/resources/aws/compute/samApplication.d.ts +15 -0
  107. package/dist/lib/resources/aws/compute/samApplication.js +27 -0
  108. package/dist/lib/resources/aws/database/clickhouseConstants.d.ts +159 -0
  109. package/dist/lib/resources/aws/database/clickhouseConstants.js +181 -0
  110. package/dist/lib/resources/aws/database/clickhouseSchemas.d.ts +71 -0
  111. package/dist/lib/resources/aws/database/clickhouseSchemas.js +157 -0
  112. package/dist/lib/resources/aws/database/clickhouseSecurityGroup.d.ts +14 -0
  113. package/dist/lib/resources/aws/database/clickhouseSecurityGroup.js +23 -0
  114. package/dist/lib/resources/aws/database/clickhouseUserData.d.ts +69 -0
  115. package/dist/lib/resources/aws/database/clickhouseUserData.js +371 -0
  116. package/dist/lib/resources/aws/database/clickhouseXmlRenderer.d.ts +56 -0
  117. package/dist/lib/resources/aws/database/clickhouseXmlRenderer.js +112 -0
  118. package/dist/lib/resources/aws/database/rdsAurora.d.ts +8 -1
  119. package/dist/lib/resources/aws/database/rdsAurora.js +42 -32
  120. package/dist/lib/resources/aws/database/rdsAuroraGlobal.d.ts +15 -2
  121. package/dist/lib/resources/aws/database/rdsAuroraGlobal.js +39 -43
  122. package/dist/lib/resources/aws/database/rdsDefaults.d.ts +6 -0
  123. package/dist/lib/resources/aws/database/rdsDefaults.js +7 -1
  124. package/dist/lib/resources/aws/database/rdsHelpers.d.ts +3 -3
  125. package/dist/lib/resources/aws/database/rdsHelpers.js +1 -0
  126. package/dist/lib/resources/aws/database/rdsInstance.d.ts +8 -1
  127. package/dist/lib/resources/aws/database/rdsInstance.js +51 -34
  128. package/dist/lib/resources/aws/database/rdsProxyOutput.d.ts +1 -1
  129. package/dist/lib/resources/aws/database/rdsProxyOutput.js +1 -1
  130. package/dist/lib/resources/aws/iam/delegationRole.js +1 -1
  131. package/dist/lib/resources/aws/iam/identityCenter/groupMembership.d.ts +9 -0
  132. package/dist/lib/resources/aws/iam/identityCenter/groupMembership.js +12 -0
  133. package/dist/lib/resources/aws/iam/identityCenter/index.d.ts +1 -0
  134. package/dist/lib/resources/aws/iam/identityCenter/index.js +1 -0
  135. package/dist/lib/resources/aws/iam/identityCenter/permissionSet.d.ts +1 -0
  136. package/dist/lib/resources/aws/iam/identityCenter/permissionSet.js +1 -0
  137. package/dist/lib/resources/aws/logging/logGroup.d.ts +0 -8
  138. package/dist/lib/resources/aws/logging/logGroup.js +0 -11
  139. package/dist/lib/resources/aws/messaging/defaultEventBus.d.ts +7 -0
  140. package/dist/lib/resources/aws/messaging/defaultEventBus.js +21 -0
  141. package/dist/lib/resources/aws/messaging/eventBridgeRule.d.ts +96 -0
  142. package/dist/lib/resources/aws/messaging/eventBridgeRule.js +110 -0
  143. package/dist/lib/resources/aws/messaging/eventTargets.d.ts +84 -0
  144. package/dist/lib/resources/aws/messaging/eventTargets.js +152 -0
  145. package/dist/lib/resources/aws/messaging/eventbridge.d.ts +25 -2
  146. package/dist/lib/resources/aws/messaging/eventbridge.js +22 -10
  147. package/dist/lib/resources/aws/messaging/index.d.ts +5 -0
  148. package/dist/lib/resources/aws/messaging/index.js +2 -0
  149. package/dist/lib/resources/aws/messaging/schedule.d.ts +118 -0
  150. package/dist/lib/resources/aws/messaging/schedule.js +64 -0
  151. package/dist/lib/resources/aws/messaging/sns.d.ts +2 -1
  152. package/dist/lib/resources/aws/messaging/sqs.d.ts +2 -1
  153. package/dist/lib/resources/aws/messaging/subscription.d.ts +112 -0
  154. package/dist/lib/resources/aws/messaging/subscription.js +67 -0
  155. package/dist/lib/resources/aws/messaging/utils.d.ts +6 -0
  156. package/dist/lib/resources/aws/messaging/utils.js +10 -0
  157. package/dist/lib/resources/aws/monitoring/clickhouseAlarms.d.ts +60 -0
  158. package/dist/lib/resources/aws/monitoring/clickhouseAlarms.js +139 -0
  159. package/dist/lib/resources/aws/monitoring/index.d.ts +2 -0
  160. package/dist/lib/resources/aws/monitoring/index.js +2 -0
  161. package/dist/lib/resources/aws/monitoring/scheduleAlarms.d.ts +47 -0
  162. package/dist/lib/resources/aws/monitoring/scheduleAlarms.js +106 -0
  163. package/dist/lib/resources/aws/networking/crossAccountDelegationRecord.js +6 -4
  164. package/dist/lib/resources/aws/networking/crossAccountReturnRoutes.js +17 -13
  165. package/dist/lib/resources/aws/networking/dnsRecord/dnsRecordBase.js +7 -5
  166. package/dist/lib/resources/aws/networking/domainCertificate.d.ts +2 -2
  167. package/dist/lib/resources/aws/networking/domainCertificate.js +6 -4
  168. package/dist/lib/resources/aws/networking/hostedZone.js +6 -5
  169. package/dist/lib/resources/aws/networking/serviceDiscovery.d.ts +96 -0
  170. package/dist/lib/resources/aws/networking/serviceDiscovery.js +96 -0
  171. package/dist/lib/resources/aws/networking/vpc.d.ts +4 -1
  172. package/dist/lib/resources/aws/networking/vpc.js +4 -1
  173. package/dist/lib/resources/aws/networking/vpcPeeringConnection.js +21 -3
  174. package/dist/lib/resources/aws/organisation/costAllocationTagActivator.d.ts +16 -5
  175. package/dist/lib/resources/aws/organisation/costAllocationTagActivator.js +17 -3
  176. package/dist/lib/resources/aws/organisation/index.d.ts +1 -1
  177. package/dist/lib/resources/aws/organisation/organisationPolicy.d.ts +2 -0
  178. package/dist/lib/resources/aws/organisation/organisationPolicy.js +3 -2
  179. package/dist/lib/resources/aws/secrets/secret.d.ts +7 -0
  180. package/dist/lib/resources/aws/secrets/secret.js +4 -3
  181. package/dist/lib/resources/aws/storage/bucketDeployment.d.ts +16 -0
  182. package/dist/lib/resources/aws/storage/bucketDeployment.js +17 -0
  183. package/dist/lib/resources/aws/storage/ecr.js +5 -5
  184. package/dist/lib/resources/aws/storage/index.d.ts +1 -0
  185. package/dist/lib/resources/aws/storage/index.js +1 -0
  186. package/dist/lib/resources/aws/storage/s3.js +10 -3
  187. package/dist/lib/resources/aws/utilities/customResource.js +18 -9
  188. package/dist/lib/synth_dump.d.ts +1 -0
  189. package/dist/lib/synth_dump.js +42 -0
  190. package/dist/lib/utils/cdkContext.d.ts +2 -0
  191. package/dist/lib/utils/cdkContext.js +4 -2
  192. package/dist/lib/utils/connections.js +6 -0
  193. package/dist/lib/utils/connector.d.ts +12 -0
  194. package/dist/lib/utils/costAllocationTags.d.ts +9 -0
  195. package/dist/lib/utils/costAllocationTags.js +11 -1
  196. package/dist/lib/utils/databaseTypes.d.ts +14 -0
  197. package/dist/lib/utils/getConfig.d.ts +2 -0
  198. package/dist/lib/utils/getConfig.js +2 -0
  199. package/dist/lib/utils/index.d.ts +1 -0
  200. package/dist/lib/utils/index.js +1 -0
  201. package/dist/lib/utils/manifestWriter.d.ts +6 -89
  202. package/dist/lib/utils/manifestWriter.js +36 -23
  203. package/dist/lib/utils/migrationVersionResolvers.d.ts +2 -0
  204. package/dist/lib/utils/migrationVersionResolvers.js +2 -0
  205. package/dist/lib/utils/orgConfigParser.js +2 -1
  206. package/dist/lib/utils/resolveAlertsTopic.d.ts +14 -0
  207. package/dist/lib/utils/resolveAlertsTopic.js +30 -0
  208. package/dist/lib/utils/validationLogger.js +6 -3
  209. package/package.json +22 -19
@@ -0,0 +1,96 @@
1
+ import { Construct } from "constructs";
2
+ import { CfnOutput, Duration } from "aws-cdk-lib";
3
+ import { PrivateDnsNamespace, DnsRecordType } from "aws-cdk-lib/aws-servicediscovery";
4
+ import { env } from "../../../utils/env.js";
5
+ import { toRemovalPolicy } from "../../../utils/removalPolicy.js";
6
+ export class ServiceDiscoveryNamespace extends Construct {
7
+ id;
8
+ #namespace;
9
+ #services = new Map();
10
+ constructor(scope, id, props) {
11
+ super(scope, id);
12
+ this.id = id;
13
+ const description = props.description ??
14
+ `ServiceDiscovery ${props.name} — Fjall private DNS namespace`;
15
+ const removalPolicyValue = props.removalPolicy ?? env({ default: "DESTROY", production: "RETAIN" });
16
+ const namespace = new PrivateDnsNamespace(this, `${id}Namespace`, {
17
+ vpc: props.vpc,
18
+ name: props.name,
19
+ description
20
+ });
21
+ this.#namespace = namespace;
22
+ namespace.applyRemovalPolicy(toRemovalPolicy(removalPolicyValue));
23
+ new CfnOutput(this, `${id}NamespaceArn`, {
24
+ key: `${id}NamespaceArn`,
25
+ value: namespace.namespaceArn,
26
+ description: `Cloud Map private DNS namespace ARN for ${id}`
27
+ });
28
+ new CfnOutput(this, `${id}NamespaceName`, {
29
+ key: `${id}NamespaceName`,
30
+ value: namespace.namespaceName,
31
+ description: `Cloud Map private DNS namespace name for ${id}`
32
+ });
33
+ }
34
+ /**
35
+ * Get the underlying CDK private DNS namespace as the `IPrivateDnsNamespace`
36
+ * interface. Per D18(a), the public surface is the interface, never the
37
+ * concrete `PrivateDnsNamespace` class.
38
+ */
39
+ getNamespace() {
40
+ return this.#namespace;
41
+ }
42
+ getNamespaceArn() {
43
+ return this.#namespace.namespaceArn;
44
+ }
45
+ getNamespaceName() {
46
+ return this.#namespace.namespaceName;
47
+ }
48
+ /**
49
+ * Register a Cloud Map service inside this namespace. Creates an
50
+ * `AWS::ServiceDiscovery::Service` resource via the underlying CDK
51
+ * `PrivateDnsNamespace.createService(...)` with Fjall description discipline
52
+ * applied. Internal-only per D7(d) — Fjall resources call this from inside
53
+ * their construct bodies; user code never invokes it directly.
54
+ *
55
+ * Throws on duplicate registration: a single namespace cannot host two
56
+ * services with the same DNS name. Caught at synth time, not deploy time.
57
+ */
58
+ registerService(props) {
59
+ if (this.#services.has(props.name)) {
60
+ throw new Error(`ServiceDiscoveryNamespace '${this.#namespace.namespaceName}': ` +
61
+ `service '${props.name}' is already registered. Each service name ` +
62
+ `must be unique within a namespace.`);
63
+ }
64
+ const description = props.description ?? `Fjall internal service: ${props.name}`;
65
+ const service = this.#namespace.createService(`Service-${props.name}`, {
66
+ name: props.name,
67
+ description,
68
+ dnsTtl: props.dnsTtl ?? Duration.seconds(60),
69
+ dnsRecordType: props.dnsRecordType === "SRV" ? DnsRecordType.SRV : DnsRecordType.A
70
+ });
71
+ this.#services.set(props.name, service);
72
+ return service;
73
+ }
74
+ /**
75
+ * True if a service with this name has been registered in the namespace.
76
+ * Cheap registry lookup; does NOT consult AWS — only counts services
77
+ * registered via `registerService(...)` on this construct.
78
+ */
79
+ hasService(name) {
80
+ return this.#services.has(name);
81
+ }
82
+ /**
83
+ * Build the FQDN for a previously-registered service:
84
+ * `<serviceName>.<namespaceName>` (e.g. `clickhouse.acme.local`). Throws if
85
+ * the service has not been registered — prevents typos that would silently
86
+ * resolve to NXDOMAIN at runtime.
87
+ */
88
+ getEndpoint(serviceName) {
89
+ if (!this.#services.has(serviceName)) {
90
+ throw new Error(`ServiceDiscoveryNamespace '${this.#namespace.namespaceName}': ` +
91
+ `service '${serviceName}' has not been registered. Call ` +
92
+ `registerService({ name: '${serviceName}' }) before resolving its endpoint.`);
93
+ }
94
+ return `${serviceName}.${this.#namespace.namespaceName}`;
95
+ }
96
+ }
@@ -1,10 +1,13 @@
1
1
  import { type Construct } from "constructs";
2
2
  import * as ec2 from "aws-cdk-lib/aws-ec2";
3
3
  import { type StackBuilder } from "../base/awsStack.js";
4
+ export declare const FLOW_LOG_TRAFFIC_TYPES: readonly ["ALL", "ACCEPT", "REJECT"];
5
+ export type FlowLogTrafficType = (typeof FLOW_LOG_TRAFFIC_TYPES)[number];
6
+ export declare const DEFAULT_MAX_AZS = 3;
4
7
  export interface VpcFlowLogConfig {
5
8
  destination?: "cloudwatch" | "s3";
6
9
  retentionDays?: number;
7
- trafficType?: "ALL" | "ACCEPT" | "REJECT";
10
+ trafficType?: FlowLogTrafficType;
8
11
  }
9
12
  export interface VpcNatConfig {
10
13
  count?: number;
@@ -3,13 +3,16 @@ import * as ec2 from "aws-cdk-lib/aws-ec2";
3
3
  import * as s3 from "aws-cdk-lib/aws-s3";
4
4
  import { LogGroup } from "../logging/logGroup.js";
5
5
  import { S3Bucket } from "../storage/index.js";
6
+ export const FLOW_LOG_TRAFFIC_TYPES = ["ALL", "ACCEPT", "REJECT"];
7
+ // Read at two sites (resources-layer constructor default + patterns-layer natGateways guard) that must agree.
8
+ export const DEFAULT_MAX_AZS = 3;
6
9
  export class Vpc extends ec2.Vpc {
7
10
  gatewayEndpoints = [];
8
11
  interfaceEndpoints = [];
9
12
  hasNatGateways;
10
13
  constructor(scope, id, props) {
11
14
  const { maxAzs: maxAzsFromProps, vpcName: explicitName, ...restProps } = props ?? {};
12
- const maxAzs = maxAzsFromProps ?? 3;
15
+ const maxAzs = maxAzsFromProps ?? DEFAULT_MAX_AZS;
13
16
  const natGateways = Vpc.resolveNatGateways(props);
14
17
  const vpcName = explicitName ?? id;
15
18
  super(scope, `Vpc${id}`, {
@@ -60,9 +60,27 @@ export class VpcPeeringConnection extends Construct {
60
60
  ])
61
61
  });
62
62
  }
63
- if (props.peerRoleArn &&
64
- props.peerRegion &&
65
- props.peerRouteTableIds &&
63
+ const hasPeerRoleArn = props.peerRoleArn !== undefined;
64
+ const hasPeerRegion = props.peerRegion !== undefined;
65
+ const hasPeerRouteTableIds = props.peerRouteTableIds !== undefined &&
66
+ props.peerRouteTableIds.length > 0;
67
+ const presentCount = Number(hasPeerRoleArn) +
68
+ Number(hasPeerRegion) +
69
+ Number(hasPeerRouteTableIds);
70
+ if (presentCount > 0 && presentCount < 3) {
71
+ const missing = [];
72
+ if (!hasPeerRoleArn)
73
+ missing.push("peerRoleArn");
74
+ if (!hasPeerRegion)
75
+ missing.push("peerRegion");
76
+ if (!hasPeerRouteTableIds)
77
+ missing.push("peerRouteTableIds");
78
+ throw new Error(`VpcPeeringConnection: cross-account return routes require all of ` +
79
+ `peerRoleArn, peerRegion, and peerRouteTableIds (non-empty). Missing: ${missing.join(", ")}.`);
80
+ }
81
+ if (props.peerRoleArn !== undefined &&
82
+ props.peerRegion !== undefined &&
83
+ props.peerRouteTableIds !== undefined &&
66
84
  props.peerRouteTableIds.length > 0) {
67
85
  new CrossAccountReturnRoutes(this, "ReturnRoutes", {
68
86
  peerRoleArn: props.peerRoleArn,
@@ -1,17 +1,28 @@
1
1
  import { Construct } from "constructs";
2
+ import { type IFunction } from "aws-cdk-lib/aws-lambda";
2
3
  import { LambdaFunction } from "../compute/lambda.js";
3
- export interface CostAllocationTagActivatorProps {
4
- /** EventBridge schedule expression. Default: "rate(24 hours)" */
5
- scheduleExpression?: string;
6
- }
7
4
  /**
8
5
  * Deploys a Lambda that auto-discovers and activates cost allocation tags.
9
6
  *
10
7
  * Runs daily in the management account. Any non-AWS tag applied to resources
11
8
  * (via app.addTags() or CDK aspects) is automatically activated in Cost
12
9
  * Explorer within 24 hours — no manual intervention required.
10
+ *
11
+ * The schedule is NOT wired by this construct — callers wire the cadence via
12
+ * `app.addSchedule(...)` so the EventBridge rule lives in the App's messaging
13
+ * stack alongside every other Fjall schedule (D9). The activator implements
14
+ * the structural `LambdaComputeShape` (duck-typed by `eventTargets.ts`), so it
15
+ * can be passed directly as `target` to `app.addSchedule(...)` without a
16
+ * `LambdaCompute` wrapper.
13
17
  */
14
18
  export declare class CostAllocationTagActivator extends Construct {
19
+ /** Marks this construct as a structural Lambda target for `eventTargets.ts`. */
20
+ readonly computeType: "lambda";
15
21
  readonly lambda: LambdaFunction;
16
- constructor(scope: Construct, id: string, props?: CostAllocationTagActivatorProps);
22
+ constructor(scope: Construct, id: string);
23
+ /**
24
+ * Structural `LambdaComputeShape` accessor — returns the activator's Lambda
25
+ * so `eventTargets.ts#resolveTarget` can wire it as an EventBridge target.
26
+ */
27
+ getFunction(): IFunction;
17
28
  }
@@ -8,12 +8,20 @@ import { LambdaFunction } from "../compute/lambda.js";
8
8
  * Runs daily in the management account. Any non-AWS tag applied to resources
9
9
  * (via app.addTags() or CDK aspects) is automatically activated in Cost
10
10
  * Explorer within 24 hours — no manual intervention required.
11
+ *
12
+ * The schedule is NOT wired by this construct — callers wire the cadence via
13
+ * `app.addSchedule(...)` so the EventBridge rule lives in the App's messaging
14
+ * stack alongside every other Fjall schedule (D9). The activator implements
15
+ * the structural `LambdaComputeShape` (duck-typed by `eventTargets.ts`), so it
16
+ * can be passed directly as `target` to `app.addSchedule(...)` without a
17
+ * `LambdaCompute` wrapper.
11
18
  */
12
19
  export class CostAllocationTagActivator extends Construct {
20
+ /** Marks this construct as a structural Lambda target for `eventTargets.ts`. */
21
+ computeType = "lambda";
13
22
  lambda;
14
- constructor(scope, id, props) {
23
+ constructor(scope, id) {
15
24
  super(scope, id);
16
- const schedule = props?.scheduleExpression ?? "rate(24 hours)";
17
25
  this.lambda = new LambdaFunction(this, "TagActivatorFunction", {
18
26
  runtime: Runtime.NODEJS_22_X,
19
27
  handler: "index.handler",
@@ -21,7 +29,6 @@ export class CostAllocationTagActivator extends Construct {
21
29
  lambdaDescription: "Auto-discovers and activates cost allocation tags in Cost Explorer",
22
30
  memorySize: 128,
23
31
  timeout: 60,
24
- scheduleExpression: schedule,
25
32
  inlinePolicy: [
26
33
  new PolicyStatement({
27
34
  effect: Effect.ALLOW,
@@ -34,6 +41,13 @@ export class CostAllocationTagActivator extends Construct {
34
41
  ]
35
42
  });
36
43
  }
44
+ /**
45
+ * Structural `LambdaComputeShape` accessor — returns the activator's Lambda
46
+ * so `eventTargets.ts#resolveTarget` can wire it as an EventBridge target.
47
+ */
48
+ getFunction() {
49
+ return this.lambda;
50
+ }
37
51
  }
38
52
  const HANDLER_CODE = `
39
53
  const { CostExplorerClient, ListCostAllocationTagsCommand, UpdateCostAllocationTagsStatusCommand } = require("@aws-sdk/client-cost-explorer");
@@ -2,4 +2,4 @@ export { Organisation as OrganisationResource, type OrganisationProps as Organis
2
2
  export { type OrganisationalUnitProps } from "./organisationalUnit.js";
3
3
  export { OrganisationAccount, type OrganisationAccountProps } from "./organisationAccount.js";
4
4
  export { OrganisationPolicy, type OrganisationPolicyProps, type OrganisationPolicyType } from "./organisationPolicy.js";
5
- export { CostAllocationTagActivator, type CostAllocationTagActivatorProps } from "./costAllocationTagActivator.js";
5
+ export { CostAllocationTagActivator } from "./costAllocationTagActivator.js";
@@ -1,4 +1,5 @@
1
1
  import { Construct } from "constructs";
2
+ import { type KeyValue } from "../../../types.js";
2
3
  export type OrganisationPolicyType = "SERVICE_CONTROL_POLICY" | "TAG_POLICY" | "BACKUP_POLICY" | "AISERVICES_OPT_OUT_POLICY";
3
4
  export interface OrganisationPolicyProps {
4
5
  name: string;
@@ -6,6 +7,7 @@ export interface OrganisationPolicyProps {
6
7
  content: string | Record<string, unknown>;
7
8
  description?: string;
8
9
  targetIds?: string[];
10
+ tags?: KeyValue[];
9
11
  }
10
12
  export declare class OrganisationPolicy extends Construct {
11
13
  readonly policyId: string;
@@ -10,7 +10,7 @@ export class OrganisationPolicy extends Construct {
10
10
  content = JSON.parse(props.content);
11
11
  }
12
12
  catch {
13
- throw new Error(`Invalid JSON in organisation policy "${props.name}": ${props.content.slice(0, 100)}`);
13
+ throw new Error(`Invalid JSON in organisation policy "${props.name}"`);
14
14
  }
15
15
  }
16
16
  else {
@@ -21,7 +21,8 @@ export class OrganisationPolicy extends Construct {
21
21
  type: props.policyType,
22
22
  content,
23
23
  description: props.description,
24
- targetIds: props.targetIds
24
+ targetIds: props.targetIds,
25
+ tags: props.tags?.map((t) => ({ key: t.key, value: t.value }))
25
26
  });
26
27
  this.policyId = policy.attrId;
27
28
  }
@@ -8,6 +8,13 @@ interface SecretProps {
8
8
  secretObjectValue?: {
9
9
  [key: string]: SecretValue;
10
10
  };
11
+ /**
12
+ * Plain-text secret value. WARNING: embedded in the CloudFormation template
13
+ * via `SecretValue.unsafePlainText` — anyone with read access to the synth
14
+ * output, the deploy artefact, or the CFN stack template can recover it.
15
+ * Prefer `generateSecretString` (server-generated, never leaves AWS) or
16
+ * `secretObjectValue` with `SecretValue.ssmSecure(...)` for inputs.
17
+ */
11
18
  secretStringValue?: string;
12
19
  description?: string;
13
20
  aliasName?: string;
@@ -17,15 +17,16 @@ export class Secret extends Construct {
17
17
  return;
18
18
  }
19
19
  // Create KMS key for new secrets only
20
- this.secretsCustomerManagedKey = new CustomerManagedKey(this, `${id}CustomerManagedKey`, {
20
+ const customerManagedKey = new CustomerManagedKey(this, `${id}CustomerManagedKey`, {
21
21
  aliasName: `cmk/${id}`
22
22
  });
23
+ this.secretsCustomerManagedKey = customerManagedKey;
23
24
  /**
24
25
  * If a secretStringValue is provided, use it to create the secret.
25
26
  */
26
27
  const secretStringValue = props.secretStringValue
27
28
  ? {
28
- secretStringValue: SecretValue.unsafePlainText(props.secretStringValue || "")
29
+ secretStringValue: SecretValue.unsafePlainText(props.secretStringValue)
29
30
  }
30
31
  : {};
31
32
  /**
@@ -47,7 +48,7 @@ export class Secret extends Construct {
47
48
  const secretOptions = {
48
49
  secretName: props.secretName,
49
50
  secretObjectValue: props.secretObjectValue,
50
- encryptionKey: this.secretsCustomerManagedKey.key,
51
+ encryptionKey: customerManagedKey.key,
51
52
  description: props.description,
52
53
  ...secretStringValue,
53
54
  ...generateSecretString,
@@ -0,0 +1,16 @@
1
+ import { BucketDeployment as CdkBucketDeployment, type BucketDeploymentProps } from "aws-cdk-lib/aws-s3-deployment";
2
+ import { type Construct } from "constructs";
3
+ /**
4
+ * Routing-point wrapper for `aws-cdk-lib/aws-s3-deployment.BucketDeployment`.
5
+ *
6
+ * Currently a thin pass-through — no defaults are applied today. The wrapper
7
+ * exists so that future SOC2 / compliance defaults (tags, retention, encryption
8
+ * on the underlying Lambda + Custom Resource) reach every consumer in one edit.
9
+ * Per `.claude/rules/generator-standards.md § Wrapper Routing Discipline` and
10
+ * `patterns/infrastructure-wrapper-routing-pattern.md § A3` ("a wrapper that
11
+ * is a thin pass-through today is still the routing point"), `lib/{config,
12
+ * patterns}/` MUST instantiate this class rather than the raw CDK construct.
13
+ */
14
+ export declare class BucketDeployment extends CdkBucketDeployment {
15
+ constructor(scope: Construct, id: string, props: BucketDeploymentProps);
16
+ }
@@ -0,0 +1,17 @@
1
+ import { BucketDeployment as CdkBucketDeployment } from "aws-cdk-lib/aws-s3-deployment";
2
+ /**
3
+ * Routing-point wrapper for `aws-cdk-lib/aws-s3-deployment.BucketDeployment`.
4
+ *
5
+ * Currently a thin pass-through — no defaults are applied today. The wrapper
6
+ * exists so that future SOC2 / compliance defaults (tags, retention, encryption
7
+ * on the underlying Lambda + Custom Resource) reach every consumer in one edit.
8
+ * Per `.claude/rules/generator-standards.md § Wrapper Routing Discipline` and
9
+ * `patterns/infrastructure-wrapper-routing-pattern.md § A3` ("a wrapper that
10
+ * is a thin pass-through today is still the routing point"), `lib/{config,
11
+ * patterns}/` MUST instantiate this class rather than the raw CDK construct.
12
+ */
13
+ export class BucketDeployment extends CdkBucketDeployment {
14
+ constructor(scope, id, props) {
15
+ super(scope, id, props);
16
+ }
17
+ }
@@ -24,14 +24,14 @@ export class Ecr extends Repository {
24
24
  });
25
25
  }
26
26
  static getRepositoryProps(props) {
27
- // todo: lifeCycleRules
28
- // todo: Encryption & EncryptionKey (default is AWS managed KMS key)
27
+ // Gotcha: DESTROY + emptyOnDelete:true silently wipes every pushed image
28
+ // on stack teardown next deploy pulls "not found" for any referenced tag.
29
29
  return {
30
30
  ...(props?.repositoryName && { repositoryName: props.repositoryName }),
31
31
  imageScanOnPush: true,
32
- imageTagMutability: TagMutability.MUTABLE,
33
- emptyOnDelete: true,
34
- removalPolicy: RemovalPolicy.DESTROY
32
+ imageTagMutability: TagMutability.IMMUTABLE,
33
+ emptyOnDelete: false,
34
+ removalPolicy: RemovalPolicy.RETAIN
35
35
  };
36
36
  }
37
37
  static build(id, props) {
@@ -1,2 +1,3 @@
1
1
  export * from "./s3.js";
2
2
  export * from "./ecr.js";
3
+ export * from "./bucketDeployment.js";
@@ -1,2 +1,3 @@
1
1
  export * from "./s3.js";
2
2
  export * from "./ecr.js";
3
+ export * from "./bucketDeployment.js";
@@ -1,5 +1,6 @@
1
1
  import { CfnOutput, Duration, RemovalPolicy } from "aws-cdk-lib";
2
2
  import { BlockPublicAccess, Bucket } from "aws-cdk-lib/aws-s3";
3
+ import { RegionInfo } from "aws-cdk-lib/region-info";
3
4
  import { toPascalCase } from "../../../utils/capitaliseString.js";
4
5
  function shouldAutoVersion(tier) {
5
6
  return tier === "resilient" || tier === "enterprise";
@@ -36,18 +37,24 @@ export class S3Bucket extends Bucket {
36
37
  });
37
38
  this.backupVaultTier = backupVaultTier;
38
39
  if (websiteHosting) {
39
- const safeBucket = toPascalCase((props.bucketName ?? id).replace(/[^A-Za-z0-9]/g, ""));
40
+ const safeBucket = toPascalCase((props.bucketName ?? id).replace(/[^A-Za-z0-9-]/g, ""));
40
41
  new CfnOutput(this, `${safeBucket}WebsiteEndpoint`, {
41
42
  key: `${safeBucket}WebsiteEndpoint`,
42
43
  exportName: `${safeBucket}WebsiteEndpoint`,
43
44
  value: this.bucketWebsiteDomainName,
44
45
  description: `S3 website endpoint for ${id} (consumed by Domain alias targets)`
45
46
  });
47
+ const region = this.stack.region;
48
+ const websiteHostedZoneId = RegionInfo.get(region).s3StaticWebsiteHostedZoneId;
49
+ if (websiteHostedZoneId === undefined) {
50
+ throw new Error(`S3 website hosted zone id is not known for region "${region}". ` +
51
+ `Update aws-cdk-lib/region-info or provide the bucket's hosted zone id explicitly.`);
52
+ }
46
53
  new CfnOutput(this, `${safeBucket}WebsiteHostedZoneId`, {
47
54
  key: `${safeBucket}WebsiteHostedZoneId`,
48
55
  exportName: `${safeBucket}WebsiteHostedZoneId`,
49
- value: this.stack.region,
50
- description: `AWS region for ${id}; consumer maps to S3 website Route53 zone ID`
56
+ value: websiteHostedZoneId,
57
+ description: `Route 53 hosted zone id for the S3 website endpoint of ${id}`
51
58
  });
52
59
  }
53
60
  }
@@ -3,6 +3,7 @@ import { Provider } from "aws-cdk-lib/custom-resources";
3
3
  import { Code } from "aws-cdk-lib/aws-lambda";
4
4
  import { Construct } from "constructs";
5
5
  import { SingletonFunction, LambdaFunction } from "../compute/lambda.js";
6
+ import { DEFAULT_CUSTOM_RESOURCE_TIMEOUT_SECONDS } from "../compute/ecsConstants.js";
6
7
  export class CustomResource extends Construct {
7
8
  response;
8
9
  resource;
@@ -15,12 +16,20 @@ export class CustomResource extends Construct {
15
16
  if (props.codePath && props.inlineCode) {
16
17
  throw new Error("CustomResource requires exactly one of codePath or inlineCode, not both");
17
18
  }
18
- const isInlineCode = !!props.inlineCode;
19
- const code = isInlineCode
20
- ? Code.fromInline(props.inlineCode)
21
- : Code.fromAsset(props.codePath, props.assetOptions);
19
+ const isInlineCode = props.inlineCode !== undefined;
20
+ let code;
21
+ if (props.inlineCode !== undefined) {
22
+ code = Code.fromInline(props.inlineCode);
23
+ }
24
+ else if (props.codePath !== undefined) {
25
+ code = Code.fromAsset(props.codePath, props.assetOptions);
26
+ }
27
+ else {
28
+ // Unreachable — earlier validation rejects this case
29
+ throw new Error("CustomResource requires codePath or inlineCode");
30
+ }
22
31
  const handler = props.handler ?? (isInlineCode ? "index.handler" : `${id}.handler`);
23
- const timeout = props.timeout?.toSeconds() ?? 300;
32
+ const timeout = props.timeout?.toSeconds() ?? DEFAULT_CUSTOM_RESOURCE_TIMEOUT_SECONDS;
24
33
  // Use LambdaFunction for inline code (unique per use), SingletonFunction for assets (shared)
25
34
  const lambdaFunction = isInlineCode
26
35
  ? new LambdaFunction(this, `${id}Lambda`, {
@@ -28,16 +37,16 @@ export class CustomResource extends Construct {
28
37
  handler,
29
38
  runtime: props.runtime,
30
39
  timeout,
31
- lambdaDescription: props.lambdaDescription || `${id} lambda`,
32
- roleDescription: props.roleDescription || `${id} custom resource lambda`,
40
+ lambdaDescription: props.lambdaDescription ?? `${id} lambda`,
41
+ roleDescription: props.roleDescription ?? `${id} custom resource lambda`,
33
42
  inlinePolicy: props.inlinePolicy
34
43
  })
35
44
  : new SingletonFunction(this, `${id}Lambda`, {
36
45
  code,
37
46
  handler,
38
47
  runtime: props.runtime,
39
- lambdaDescription: props.lambdaDescription || `${id} lambda`,
40
- roleDescription: props.roleDescription || `${id} custom resource lambda`,
48
+ lambdaDescription: props.lambdaDescription ?? `${id} lambda`,
49
+ roleDescription: props.roleDescription ?? `${id} custom resource lambda`,
41
50
  inlinePolicy: props.inlinePolicy
42
51
  });
43
52
  const provider = new Provider(this, `${id}Provider`, {
@@ -0,0 +1 @@
1
+ export {};
@@ -0,0 +1,42 @@
1
+ import { App, Stack } from "aws-cdk-lib";
2
+ import { Template } from "aws-cdk-lib/assertions";
3
+ import { Vpc } from "aws-cdk-lib/aws-ec2";
4
+ import { Repository } from "aws-cdk-lib/aws-ecr";
5
+ import { mkdtempSync, mkdirSync } from "node:fs";
6
+ import { tmpdir } from "node:os";
7
+ import { join } from "node:path";
8
+ import { EcsCompute } from "./patterns/aws/computeEcs.js";
9
+ import { RelationalDatabase } from "./patterns/aws/database.js";
10
+ const root = mkdtempSync(join(tmpdir(), "ds-"));
11
+ mkdirSync(join(root, "20260512000000_init"));
12
+ const app = new App();
13
+ const stack = new Stack(app, "S", { env: { account: "123456789012", region: "us-east-1" } });
14
+ const vpc = new Vpc(stack, "Vpc");
15
+ const db = new RelationalDatabase(stack, "Db", {
16
+ type: "Instance", vpc, databaseName: "appdata",
17
+ migrations: { tool: "prisma", dir: root }
18
+ });
19
+ new EcsCompute(stack, "Cluster", {
20
+ type: "ecs",
21
+ ecrRepository: new Repository(stack, "Ecr"),
22
+ services: [{
23
+ name: "web", capacityProvider: "FARGATE",
24
+ containers: [{ name: "app", image: "nginx:latest", port: 3000 }],
25
+ connections: [db],
26
+ migrations: {
27
+ mode: "lifecycle-hook", command: ["node", "migrate.mjs"], entryPoint: ["/usr/bin/tini", "--"],
28
+ separateTaskDef: { cpu: 512, memoryLimitMiB: 1024 }
29
+ }
30
+ }]
31
+ });
32
+ const t = Template.fromStack(stack);
33
+ const ing = t.findResources("AWS::EC2::SecurityGroupIngress");
34
+ for (const [name, res] of Object.entries(ing)) {
35
+ console.log("INGRESS:", name);
36
+ console.log(" props:", JSON.stringify(res.Properties));
37
+ }
38
+ const eg = t.findResources("AWS::EC2::SecurityGroupEgress");
39
+ for (const [name, res] of Object.entries(eg)) {
40
+ console.log("EGRESS:", name);
41
+ console.log(" props:", JSON.stringify(res.Properties));
42
+ }
@@ -1,6 +1,8 @@
1
1
  import type { Node } from "constructs";
2
2
  export declare const CDK_CONTEXT_KEYS: {
3
3
  readonly ORG_ID: "orgId";
4
+ readonly ROOT_ID: "rootId";
5
+ readonly MANAGEMENT_ACCOUNT_ID: "managementAccountId";
4
6
  };
5
7
  export declare const DEFAULT_ORG_ID: "default";
6
8
  export declare function resolveOrgId(node: Node, fallback: string): string;
@@ -1,10 +1,12 @@
1
1
  export const CDK_CONTEXT_KEYS = {
2
- ORG_ID: "orgId"
2
+ ORG_ID: "orgId",
3
+ ROOT_ID: "rootId",
4
+ MANAGEMENT_ACCOUNT_ID: "managementAccountId"
3
5
  };
4
6
  export const DEFAULT_ORG_ID = "default";
5
7
  export function resolveOrgId(node, fallback) {
6
8
  const raw = node.tryGetContext(CDK_CONTEXT_KEYS.ORG_ID);
7
- return typeof raw === "string" ? raw : fallback;
9
+ return typeof raw === "string" && raw !== "" ? raw : fallback;
8
10
  }
9
11
  export function buildSsmPrefix(orgId, appName) {
10
12
  return `/fjall/${orgId}/${appName}`;
@@ -21,6 +21,7 @@
21
21
  * this.lambdaFunction // IConnectable (security group)
22
22
  * );
23
23
  */
24
+ import { Port } from "aws-cdk-lib/aws-ec2";
24
25
  import { CONNECTION_TYPE, isConnectionConfig, isConnector, isConnectable, isConnectionAccess, isMessagingAccess } from "./connector.js";
25
26
  /** Default access levels for each connector type. */
26
27
  const DEFAULT_ACCESS = {
@@ -140,6 +141,11 @@ export function processConnections(connections, grantee, connectable) {
140
141
  case "relational": {
141
142
  requireConnectable(connectable, `${resource.connectorType} connector`);
142
143
  connectable.connections.allowToDefaultPort(resource);
144
+ if (resource.additionalTcpPorts !== undefined) {
145
+ for (const port of resource.additionalTcpPorts) {
146
+ connectable.connections.allowTo(resource, Port.tcp(port));
147
+ }
148
+ }
143
149
  return buildSecurityGroupResult(resource);
144
150
  }
145
151
  case "remote": {
@@ -115,6 +115,18 @@ export interface ISecurityGroupConnector extends IConnector {
115
115
  readonly connectorType: "securityGroup" | "relational";
116
116
  /** The security group connections for this resource. */
117
117
  readonly connections: IConnectable["connections"];
118
+ /**
119
+ * Additional TCP ports to open beyond the default port carried by
120
+ * `connections`. Used by dual-port resources such as ClickHouse, where the
121
+ * primary port (HTTP, 8123) flows through `connections.allowDefaultPortFrom`
122
+ * and the native protocol port (9000) is appended here. Optional; existing
123
+ * single-port consumers (Aurora, Instance, GlobalAurora) leave this
124
+ * undefined and behave unchanged.
125
+ *
126
+ * Surfaced as `number[]` (not `aws-cdk-lib/aws-ec2.Port[]`) per the
127
+ * 2026-05-05 ClickHouse design D18(k); `Port.tcp(n)` is plumbing.
128
+ */
129
+ readonly additionalTcpPorts?: number[];
118
130
  }
119
131
  /**
120
132
  * Remote connector interface.
@@ -1,6 +1,15 @@
1
+ import type { IConstruct } from "constructs";
1
2
  export declare const COST_ALLOCATION_TAGS: {
2
3
  readonly ENVIRONMENT: "fjall:costAllocation:environment";
3
4
  readonly SERVICE: "fjall:costAllocation:service";
4
5
  readonly DOMAIN: "fjall:costAllocation:domain";
6
+ readonly OWNER: "fjall:costAllocation:owner";
5
7
  };
6
8
  export declare const DEFAULT_COST_ALLOCATION_ENVIRONMENT: "management";
9
+ export interface CostAllocationTagsArgs {
10
+ readonly service: string;
11
+ readonly domain: string;
12
+ readonly environment?: string;
13
+ readonly owner?: string;
14
+ }
15
+ export declare function applyCostAllocationTags(scope: IConstruct, args: CostAllocationTagsArgs): void;
@@ -1,6 +1,16 @@
1
+ import { Tags } from "aws-cdk-lib";
1
2
  export const COST_ALLOCATION_TAGS = {
2
3
  ENVIRONMENT: "fjall:costAllocation:environment",
3
4
  SERVICE: "fjall:costAllocation:service",
4
- DOMAIN: "fjall:costAllocation:domain"
5
+ DOMAIN: "fjall:costAllocation:domain",
6
+ OWNER: "fjall:costAllocation:owner"
5
7
  };
6
8
  export const DEFAULT_COST_ALLOCATION_ENVIRONMENT = "management";
9
+ export function applyCostAllocationTags(scope, args) {
10
+ Tags.of(scope).add(COST_ALLOCATION_TAGS.ENVIRONMENT, args.environment ?? DEFAULT_COST_ALLOCATION_ENVIRONMENT);
11
+ Tags.of(scope).add(COST_ALLOCATION_TAGS.SERVICE, args.service);
12
+ Tags.of(scope).add(COST_ALLOCATION_TAGS.DOMAIN, args.domain);
13
+ if (args.owner !== undefined) {
14
+ Tags.of(scope).add(COST_ALLOCATION_TAGS.OWNER, args.owner);
15
+ }
16
+ }