@fjall/components-infrastructure 2.19.3 → 2.19.5
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.
|
@@ -5,6 +5,7 @@ import { ManagementEventsTrail } from "../../config/aws/cloudTrail.js";
|
|
|
5
5
|
import { OrganisationTrail } from "../../config/aws/organisationTrail.js";
|
|
6
6
|
import { ScpPreset } from "../../config/aws/scpPreset.js";
|
|
7
7
|
import { OrganisationResource, OrganisationAccount, CostAllocationTagActivator } from "../../resources/aws/organisation/index.js";
|
|
8
|
+
import { Schedule } from "../../resources/aws/messaging/schedule.js";
|
|
8
9
|
import { stripAndCamelCase } from "../../utils/stripAndCamelCase.js";
|
|
9
10
|
import { CDK_CONTEXT_KEYS, resolveAccountTrailState } from "../../utils/cdkContext.js";
|
|
10
11
|
import { getConfig } from "../../utils/getConfig.js";
|
|
@@ -134,13 +135,26 @@ export class Organisation extends Account {
|
|
|
134
135
|
}
|
|
135
136
|
setupOrganisationFeatures(identityCenter, managementAccountId) {
|
|
136
137
|
this.setupIdentityCenter(identityCenter, managementAccountId);
|
|
137
|
-
|
|
138
|
+
// Home region only: Cost Explorer is a global service (single us-east-1
|
|
139
|
+
// endpoint), so an org-region cascade stack would otherwise synthesise a
|
|
140
|
+
// redundant daily activator Lambda per region against the same global
|
|
141
|
+
// endpoint — mirrors the OrganisationTrail home-region guard above.
|
|
142
|
+
if (!this.accountGlobalsConfigured) {
|
|
143
|
+
this.setupCostAllocationTagActivator();
|
|
144
|
+
}
|
|
138
145
|
}
|
|
139
146
|
setupCostAllocationTagActivator() {
|
|
140
147
|
const activator = new CostAllocationTagActivator(this, "CostAllocationTagActivator");
|
|
141
|
-
|
|
148
|
+
// Construct the schedule in THIS (the Organisation) stack rather than via
|
|
149
|
+
// App.addSchedule(...), which places the EventBridge rule + invoke
|
|
150
|
+
// permission in the App messaging stack — a stack the org deploy never
|
|
151
|
+
// targets, so the trigger never deploys (the Lambda exists but is never
|
|
152
|
+
// invoked). Co-locating the rule here means one `cdk deploy Organisation`
|
|
153
|
+
// ships the activator and its daily trigger together.
|
|
154
|
+
new Schedule(this, "CostAllocationTagActivatorSchedule", {
|
|
142
155
|
schedule: "rate(24 hours)",
|
|
143
|
-
target: activator
|
|
156
|
+
target: activator,
|
|
157
|
+
appName: App.getInstance().getName()
|
|
144
158
|
});
|
|
145
159
|
}
|
|
146
160
|
setupIdentityCenter(identityCenter, managementAccountId) {
|
|
@@ -4,16 +4,24 @@ import { LambdaFunction } from "../compute/lambda.js";
|
|
|
4
4
|
/**
|
|
5
5
|
* Deploys a Lambda that auto-discovers and activates cost allocation tags.
|
|
6
6
|
*
|
|
7
|
-
* Runs
|
|
8
|
-
*
|
|
9
|
-
*
|
|
7
|
+
* Runs in the management account. Any non-AWS tag applied to resources (via
|
|
8
|
+
* app.addTags() or CDK aspects) is automatically activated in Cost Explorer —
|
|
9
|
+
* no manual intervention required. Cost Explorer is a global service, so the
|
|
10
|
+
* handler pins the us-east-1 endpoint and the caller synthesises this activator
|
|
11
|
+
* in the organisation's home region only.
|
|
10
12
|
*
|
|
11
|
-
*
|
|
12
|
-
*
|
|
13
|
-
* stack
|
|
14
|
-
*
|
|
15
|
-
*
|
|
16
|
-
*
|
|
13
|
+
* Activation runs on two cadences:
|
|
14
|
+
* - A deploy-time one-shot (the `AwsCustomResource` below) invokes the Lambda
|
|
15
|
+
* on org-stack create so tags already surfaced by AWS activate immediately,
|
|
16
|
+
* without waiting for the first daily tick.
|
|
17
|
+
* - A daily schedule, wired by the caller (`organisation.ts`) as a `Schedule`
|
|
18
|
+
* constructed in the Organisation stack — co-located with this Lambda so a
|
|
19
|
+
* single `cdk deploy Organisation` deploys the EventBridge rule + invoke
|
|
20
|
+
* permission. (A cross-stack `app.addSchedule(...)` placed that rule in the
|
|
21
|
+
* App messaging stack, which the org deploy never targets, so the trigger
|
|
22
|
+
* never materialised.) The activator implements the structural
|
|
23
|
+
* `LambdaComputeShape` (duck-typed by `eventTargets.ts`), so it can be passed
|
|
24
|
+
* directly as the `Schedule` target without a `LambdaCompute` wrapper.
|
|
17
25
|
*/
|
|
18
26
|
export declare class CostAllocationTagActivator extends Construct {
|
|
19
27
|
/** Marks this construct as a structural Lambda target for `eventTargets.ts`. */
|
|
@@ -1,21 +1,31 @@
|
|
|
1
1
|
import { Construct } from "constructs";
|
|
2
2
|
import { PhysicalName } from "aws-cdk-lib";
|
|
3
|
+
import { AwsCustomResourcePolicy, PhysicalResourceId } from "aws-cdk-lib/custom-resources";
|
|
3
4
|
import { Code, Runtime } from "aws-cdk-lib/aws-lambda";
|
|
4
5
|
import { PolicyStatement, Effect } from "aws-cdk-lib/aws-iam";
|
|
5
6
|
import { LambdaFunction } from "../compute/lambda.js";
|
|
7
|
+
import { AwsCustomResource } from "../utilities/awsCustomResource.js";
|
|
6
8
|
/**
|
|
7
9
|
* Deploys a Lambda that auto-discovers and activates cost allocation tags.
|
|
8
10
|
*
|
|
9
|
-
* Runs
|
|
10
|
-
*
|
|
11
|
-
*
|
|
11
|
+
* Runs in the management account. Any non-AWS tag applied to resources (via
|
|
12
|
+
* app.addTags() or CDK aspects) is automatically activated in Cost Explorer —
|
|
13
|
+
* no manual intervention required. Cost Explorer is a global service, so the
|
|
14
|
+
* handler pins the us-east-1 endpoint and the caller synthesises this activator
|
|
15
|
+
* in the organisation's home region only.
|
|
12
16
|
*
|
|
13
|
-
*
|
|
14
|
-
*
|
|
15
|
-
* stack
|
|
16
|
-
*
|
|
17
|
-
*
|
|
18
|
-
*
|
|
17
|
+
* Activation runs on two cadences:
|
|
18
|
+
* - A deploy-time one-shot (the `AwsCustomResource` below) invokes the Lambda
|
|
19
|
+
* on org-stack create so tags already surfaced by AWS activate immediately,
|
|
20
|
+
* without waiting for the first daily tick.
|
|
21
|
+
* - A daily schedule, wired by the caller (`organisation.ts`) as a `Schedule`
|
|
22
|
+
* constructed in the Organisation stack — co-located with this Lambda so a
|
|
23
|
+
* single `cdk deploy Organisation` deploys the EventBridge rule + invoke
|
|
24
|
+
* permission. (A cross-stack `app.addSchedule(...)` placed that rule in the
|
|
25
|
+
* App messaging stack, which the org deploy never targets, so the trigger
|
|
26
|
+
* never materialised.) The activator implements the structural
|
|
27
|
+
* `LambdaComputeShape` (duck-typed by `eventTargets.ts`), so it can be passed
|
|
28
|
+
* directly as the `Schedule` target without a `LambdaCompute` wrapper.
|
|
19
29
|
*/
|
|
20
30
|
export class CostAllocationTagActivator extends Construct {
|
|
21
31
|
/** Marks this construct as a structural Lambda target for `eventTargets.ts`. */
|
|
@@ -43,6 +53,36 @@ export class CostAllocationTagActivator extends Construct {
|
|
|
43
53
|
})
|
|
44
54
|
]
|
|
45
55
|
});
|
|
56
|
+
// Deploy-time one-shot: invoke the activator on org-stack create so tags
|
|
57
|
+
// already surfaced by AWS activate immediately rather than waiting for the
|
|
58
|
+
// first daily tick. Async (`Event`) and idempotent, so it never blocks or
|
|
59
|
+
// fails the deploy; the stable physicalResourceId means it fires on create
|
|
60
|
+
// (and only re-fires if the target function is replaced).
|
|
61
|
+
const activationParameters = {
|
|
62
|
+
FunctionName: this.lambda.functionName,
|
|
63
|
+
InvocationType: "Event"
|
|
64
|
+
};
|
|
65
|
+
new AwsCustomResource(this, "DeployTimeActivation", {
|
|
66
|
+
onCreate: {
|
|
67
|
+
service: "Lambda",
|
|
68
|
+
action: "invoke",
|
|
69
|
+
parameters: activationParameters,
|
|
70
|
+
physicalResourceId: PhysicalResourceId.of("cost-allocation-tag-activation")
|
|
71
|
+
},
|
|
72
|
+
onUpdate: {
|
|
73
|
+
service: "Lambda",
|
|
74
|
+
action: "invoke",
|
|
75
|
+
parameters: activationParameters,
|
|
76
|
+
physicalResourceId: PhysicalResourceId.of("cost-allocation-tag-activation")
|
|
77
|
+
},
|
|
78
|
+
policy: AwsCustomResourcePolicy.fromStatements([
|
|
79
|
+
new PolicyStatement({
|
|
80
|
+
effect: Effect.ALLOW,
|
|
81
|
+
actions: ["lambda:InvokeFunction"],
|
|
82
|
+
resources: [this.lambda.functionArn]
|
|
83
|
+
})
|
|
84
|
+
])
|
|
85
|
+
});
|
|
46
86
|
}
|
|
47
87
|
/**
|
|
48
88
|
* Structural `LambdaComputeShape` accessor — returns the activator's Lambda
|
|
@@ -56,7 +96,8 @@ const HANDLER_CODE = `
|
|
|
56
96
|
const { CostExplorerClient, ListCostAllocationTagsCommand, UpdateCostAllocationTagsStatusCommand } = require("@aws-sdk/client-cost-explorer");
|
|
57
97
|
|
|
58
98
|
exports.handler = async () => {
|
|
59
|
-
|
|
99
|
+
// Cost Explorer is a global service reachable only via the us-east-1 endpoint.
|
|
100
|
+
const client = new CostExplorerClient({ region: "us-east-1" });
|
|
60
101
|
const { CostAllocationTags = [] } = await client.send(new ListCostAllocationTagsCommand({ MaxResults: 1000 }));
|
|
61
102
|
|
|
62
103
|
const inactive = CostAllocationTags.filter(
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@fjall/components-infrastructure",
|
|
3
|
-
"version": "2.19.
|
|
3
|
+
"version": "2.19.5",
|
|
4
4
|
"license": "SEE LICENSE IN LICENSE",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"bin": {
|
|
@@ -62,8 +62,8 @@
|
|
|
62
62
|
},
|
|
63
63
|
"dependencies": {
|
|
64
64
|
"@aws-sdk/client-organizations": "^3.1038.0",
|
|
65
|
-
"@fjall/generator": "^2.19.
|
|
66
|
-
"@fjall/util": "^2.19.
|
|
65
|
+
"@fjall/generator": "^2.19.5",
|
|
66
|
+
"@fjall/util": "^2.19.5",
|
|
67
67
|
"constructs": "^10.0.0"
|
|
68
68
|
},
|
|
69
69
|
"overrides": {
|
|
@@ -77,5 +77,5 @@
|
|
|
77
77
|
"engines": {
|
|
78
78
|
"node": ">=18.0.0"
|
|
79
79
|
},
|
|
80
|
-
"gitHead": "
|
|
80
|
+
"gitHead": "8179898f55c1467404e6a715ead5404404e45be0"
|
|
81
81
|
}
|