@infoxchange/make-it-so 2.18.0 → 2.18.1-internal-testing-add-tag-setup-6f9bc82.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.
package/README.md CHANGED
@@ -232,6 +232,70 @@ const domainCert = new IxCertificate(scope, "ExampleDotComCertificate", {
232
232
 
233
233
  </details>
234
234
 
235
+ <details>
236
+ <summary><strong>IxCloudWatchAlarm</strong> - Creates a CloudWatch alarm with IX-specific defaults.</summary>
237
+
238
+ IxCloudWatchAlarm extends AWS CDK's CloudWatch Alarm functionality with IX-specific defaults and special handling for CloudFront alarms (which must be created in us-east-1). If no actions are specified, the alarm will automatically use the IX alarm SNS topic.
239
+
240
+ The construct also supports a `toNotify` property that allows you to specify email addresses or other notification identifiers, which will be added to the alarm description for IX's monitoring system.
241
+
242
+ ```typescript
243
+ import { IxCloudWatchAlarm } from "@infoxchange/make-it-so/cdk-constructs";
244
+
245
+ new IxCloudWatchAlarm(scope, "ApiErrorAlarm", {
246
+ alarmName: "high-error-rate",
247
+ metric: {
248
+ namespace: "AWS/ApiGateway",
249
+ metricName: "5XXError",
250
+ dimensionsMap: {
251
+ ApiName: "my-api",
252
+ },
253
+ period: (Duration) => Duration.minutes(5),
254
+ statistic: (Stats) => Stats.AVERAGE,
255
+ },
256
+ threshold: 10,
257
+ evaluationPeriods: 2,
258
+ comparisonOperator: (ComparisonOperator) =>
259
+ ComparisonOperator.GREATER_THAN_THRESHOLD,
260
+ toNotify: ["team@example.com"],
261
+ alarmDescription: "Alert when API error rate is too high",
262
+ });
263
+ ```
264
+
265
+ #### Options:
266
+
267
+ | Prop | Type | Description |
268
+ | -------------------------- | ------------------------------------------------------------------ | -------------------------------------------------------------------------------------------------------------------------------------------------- |
269
+ | metric | object | Metric configuration |
270
+ | metric.namespace | string | The namespace of the metric (e.g., "AWS/ApiGateway") |
271
+ | metric.metricName | string | The name of the metric |
272
+ | metric.dimensionsMap | Record<string, string> | (optional) Dimensions for the metric |
273
+ | metric.period | Duration \| ((Duration) => Duration) | (optional) The period over which the statistic is applied. Can be a function for easier access to CDK Duration helpers |
274
+ | metric.statistic | string \| ((Stats) => string) | (optional) The statistic to apply (e.g., "Average", "Sum"). Can be a function for easier access to CloudWatch.Stats helpers |
275
+ | comparisonOperator | ComparisonOperator \| ((ComparisonOperator) => ComparisonOperator) | How to compare the metric to the threshold. Can be a function for easier access to CloudWatch.ComparisonOperator helpers |
276
+ | threshold | number | The value to compare the metric against |
277
+ | evaluationPeriods | number | The number of periods over which data is compared to the threshold |
278
+ | treatMissingData | TreatMissingData \| ((TreatMissingData) => TreatMissingData) | (optional) How to treat missing data points. Can be a function for easier access to CloudWatch.TreatMissingData helpers |
279
+ | alarmName | string | (optional) Name of the alarm |
280
+ | alarmDescription | string | (optional) Description of the alarm |
281
+ | toNotify | string[] | (optional) List of email addresses or notification identifiers to be notified when the alarm triggers. This will be added to the alarm description |
282
+ | actions | object | (optional) Actions to take when alarm state changes. If not provided, defaults to IX alarm SNS topic |
283
+ | actions.onOk | (string \| IAlarmAction)[] | (optional) Actions to take when alarm goes to OK state |
284
+ | actions.onAlarm | (string \| IAlarmAction)[] | (optional) Actions to take when alarm goes to ALARM state |
285
+ | actions.onInsufficientData | (string \| IAlarmAction)[] | (optional) Actions to take when alarm goes to INSUFFICIENT_DATA state |
286
+ | [...CloudWatch.AlarmProps] | | Any other props accepted by [CloudWatch.Alarm](https://docs.aws.amazon.com/cdk/api/v2/docs/aws-cdk-lib.aws_cloudwatch.Alarm.html) |
287
+
288
+ #### Static Properties:
289
+
290
+ IxCloudWatchAlarm provides convenient static access to CloudWatch constants:
291
+
292
+ - `IxCloudWatchAlarm.Stats` - CloudWatch.Stats for metric statistics
293
+ - `IxCloudWatchAlarm.Duration` - CDK.Duration for time periods
294
+ - `IxCloudWatchAlarm.TreatMissingData` - CloudWatch.TreatMissingData for handling missing data
295
+ - `IxCloudWatchAlarm.ComparisonOperator` - CloudWatch.ComparisonOperator for comparison operations
296
+
297
+ </details>
298
+
235
299
  <details>
236
300
  <summary><strong>IxDnsRecord</strong> - Creates a DNS record for a domain managed by IX.</summary>
237
301
 
@@ -16,6 +16,7 @@ declare const _default: {
16
16
  clamAVUrl: string;
17
17
  vpcHttpProxy: string;
18
18
  alarmSnsTopic: string;
19
+ tags: Record<string, string>;
19
20
  } | {
20
21
  isIxDeploy: false;
21
22
  appName: string;
@@ -32,6 +33,7 @@ declare const _default: {
32
33
  clamAVUrl: string;
33
34
  vpcHttpProxy: string;
34
35
  alarmSnsTopic: string;
36
+ tags: Record<string, string>;
35
37
  isInternalApp?: boolean | undefined;
36
38
  smtpPort?: number | undefined;
37
39
  };
@@ -54,6 +56,7 @@ export declare const getDeployConfig: () => {
54
56
  clamAVUrl: string;
55
57
  vpcHttpProxy: string;
56
58
  alarmSnsTopic: string;
59
+ tags: Record<string, string>;
57
60
  } | {
58
61
  isIxDeploy: false;
59
62
  appName: string;
@@ -70,6 +73,7 @@ export declare const getDeployConfig: () => {
70
73
  clamAVUrl: string;
71
74
  vpcHttpProxy: string;
72
75
  alarmSnsTopic: string;
76
+ tags: Record<string, string>;
73
77
  isInternalApp?: boolean | undefined;
74
78
  smtpPort?: number | undefined;
75
79
  };
@@ -1 +1 @@
1
- {"version":3,"file":"deployConfig.d.ts","sourceRoot":"","sources":["../src/deployConfig.ts"],"names":[],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAsFA,wBAA0C;AAG1C,eAAO,MAAM,eAAe;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;CAAmC,CAAC"}
1
+ {"version":3,"file":"deployConfig.d.ts","sourceRoot":"","sources":["../src/deployConfig.ts"],"names":[],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAyFA,wBAA0C;AAG1C,eAAO,MAAM,eAAe;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;CAAmC,CAAC"}
@@ -17,6 +17,7 @@ const getEnvVars = () => ({
17
17
  clamAVUrl: process.env.CLAMAV_URL ?? "",
18
18
  vpcHttpProxy: process.env.VPC_HTTP_PROXY ?? "",
19
19
  alarmSnsTopic: process.env.IX_ALARM_SNS_TOPIC ?? "",
20
+ tags: JSON.parse(process.env.IX_TAGS ?? "{}"),
20
21
  });
21
22
  const ixDeployConfigSchema = z
22
23
  .object({
@@ -41,6 +42,7 @@ const ixDeployConfigSchema = z
41
42
  clamAVUrl: z.string().url(),
42
43
  vpcHttpProxy: z.string().url(),
43
44
  alarmSnsTopic: z.string().min(1),
45
+ tags: z.record(z.string(), z.string()),
44
46
  })
45
47
  .strip();
46
48
  const nonIxDeployConfigSchema = z
@@ -70,6 +72,7 @@ const nonIxDeployConfigSchema = z
70
72
  clamAVUrl: z.string(),
71
73
  vpcHttpProxy: z.string(),
72
74
  alarmSnsTopic: z.string(),
75
+ tags: z.record(z.string(), z.string()),
73
76
  })
74
77
  .strip();
75
78
  const schema = z.discriminatedUnion("isIxDeploy", [
@@ -0,0 +1,11 @@
1
+ import { IAspect } from "aws-cdk-lib";
2
+ import { IConstruct } from "constructs";
3
+ export declare class ConditionalTags implements IAspect {
4
+ private getTags;
5
+ constructor(getTags: (node: IConstruct) => Array<{
6
+ key: string;
7
+ value: string;
8
+ }> | undefined | null);
9
+ visit(node: IConstruct): void;
10
+ }
11
+ //# sourceMappingURL=ConditionalTags.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"ConditionalTags.d.ts","sourceRoot":"","sources":["../../../src/lib/tags/ConditionalTags.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,OAAO,EAAQ,MAAM,aAAa,CAAC;AAC5C,OAAO,EAAE,UAAU,EAAE,MAAM,YAAY,CAAC;AAExC,qBAAa,eAAgB,YAAW,OAAO;IAE3C,OAAO,CAAC,OAAO;gBAAP,OAAO,EAAE,CACf,IAAI,EAAE,UAAU,KACb,KAAK,CAAC;QAAE,GAAG,EAAE,MAAM,CAAC;QAAC,KAAK,EAAE,MAAM,CAAA;KAAE,CAAC,GAAG,SAAS,GAAG,IAAI;IAG/D,KAAK,CAAC,IAAI,EAAE,UAAU;CAKvB"}
@@ -0,0 +1,12 @@
1
+ import { Tags } from "aws-cdk-lib";
2
+ export class ConditionalTags {
3
+ getTags;
4
+ constructor(getTags) {
5
+ this.getTags = getTags;
6
+ }
7
+ visit(node) {
8
+ this.getTags(node)?.forEach(({ key, value }) => {
9
+ Tags.of(node).add(key, value);
10
+ });
11
+ }
12
+ }
@@ -0,0 +1,3 @@
1
+ export { setupTags } from "./setupTags.js";
2
+ export { ConditionalTags } from "./ConditionalTags.js";
3
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/lib/tags/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,SAAS,EAAE,MAAM,gBAAgB,CAAC;AAC3C,OAAO,EAAE,eAAe,EAAE,MAAM,sBAAsB,CAAC"}
@@ -0,0 +1,2 @@
1
+ export { setupTags } from "./setupTags.js";
2
+ export { ConditionalTags } from "./ConditionalTags.js";
@@ -0,0 +1,18 @@
1
+ import { IConstruct } from "constructs";
2
+ export type ModifyTagsProps = {
3
+ node: IConstruct;
4
+ isLeafNode: boolean;
5
+ isRootNode: boolean;
6
+ currentTags: Array<{
7
+ key: string;
8
+ value: string;
9
+ }>;
10
+ };
11
+ export type SetupTagsOptions = {
12
+ modifyTags?: (props: ModifyTagsProps) => Array<{
13
+ key: string;
14
+ value: string;
15
+ }>;
16
+ };
17
+ export declare function setupTags(scope: IConstruct, { modifyTags }?: SetupTagsOptions): void;
18
+ //# sourceMappingURL=setupTags.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"setupTags.d.ts","sourceRoot":"","sources":["../../../src/lib/tags/setupTags.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,UAAU,EAAE,MAAM,YAAY,CAAC;AAKxC,MAAM,MAAM,eAAe,GAAG;IAC5B,IAAI,EAAE,UAAU,CAAC;IACjB,UAAU,EAAE,OAAO,CAAC;IACpB,UAAU,EAAE,OAAO,CAAC;IACpB,WAAW,EAAE,KAAK,CAAC;QAAE,GAAG,EAAE,MAAM,CAAC;QAAC,KAAK,EAAE,MAAM,CAAA;KAAE,CAAC,CAAC;CACpD,CAAC;AAEF,MAAM,MAAM,gBAAgB,GAAG;IAC7B,UAAU,CAAC,EAAE,CACX,KAAK,EAAE,eAAe,KACnB,KAAK,CAAC;QAAE,GAAG,EAAE,MAAM,CAAC;QAAC,KAAK,EAAE,MAAM,CAAA;KAAE,CAAC,CAAC;CAC5C,CAAC;AAEF,wBAAgB,SAAS,CACvB,KAAK,EAAE,UAAU,EACjB,EAAE,UAAU,EAAE,GAAE,gBAAqB,QAiCtC"}
@@ -0,0 +1,33 @@
1
+ import { Aspects } from "aws-cdk-lib";
2
+ import { Function } from "sst/constructs";
3
+ import { ConditionalTags } from "./ConditionalTags.js";
4
+ import deployConfig from "../../deployConfig.js";
5
+ export function setupTags(scope, { modifyTags } = {}) {
6
+ const conditionalTags = new ConditionalTags((node) => {
7
+ let tags = [];
8
+ const isLeafNode = node.node.children.length === 0;
9
+ const isRootNode = node === scope;
10
+ // Add tags from deploy config to all constructs
11
+ if (isRootNode) {
12
+ Object.entries(deployConfig.tags).forEach(([key, value]) => {
13
+ tags.push({ key, value });
14
+ });
15
+ }
16
+ // SST v2's live lambda feature means that the local machine that `sst dev` is run on may use AWS creds that were
17
+ // setup for a lambda. This triggers a false positive in GuardDuty, so we suppress that finding for any lambda that
18
+ // has live dev enabled.
19
+ if (node instanceof Function && node._isLiveDevEnabled) {
20
+ tags.push({ key: "guardduty-suppress", value: "true" });
21
+ }
22
+ tags = modifyTags
23
+ ? modifyTags({
24
+ node,
25
+ isLeafNode,
26
+ isRootNode,
27
+ currentTags: tags,
28
+ })
29
+ : tags;
30
+ return tags;
31
+ });
32
+ Aspects.of(scope).add(conditionalTags);
33
+ }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@infoxchange/make-it-so",
3
- "version": "2.18.0",
3
+ "version": "2.18.1-internal-testing-add-tag-setup-6f9bc82.1",
4
4
  "description": "Makes deploying services to IX infra easy",
5
5
  "repository": "github:infoxchange/make-it-so",
6
6
  "publishConfig": {
@@ -21,7 +21,8 @@
21
21
  "./cdk-constructs": "./dist/cdk-constructs/index.js",
22
22
  "./deployConfig": "./dist/deployConfig.js",
23
23
  "./auth": "./dist/lib/auth/index.js",
24
- "./proxy": "./dist/lib/proxy/index.js"
24
+ "./proxy": "./dist/lib/proxy/index.js",
25
+ "./tags": "./dist/lib/tags/index.js"
25
26
  },
26
27
  "lint-staged": {
27
28
  "**/*": [
@@ -19,6 +19,7 @@ const getEnvVars = () =>
19
19
  clamAVUrl: process.env.CLAMAV_URL ?? "",
20
20
  vpcHttpProxy: process.env.VPC_HTTP_PROXY ?? "",
21
21
  alarmSnsTopic: process.env.IX_ALARM_SNS_TOPIC ?? "",
22
+ tags: JSON.parse(process.env.IX_TAGS ?? "{}"),
22
23
  }) satisfies Record<string, string | boolean>;
23
24
 
24
25
  const ixDeployConfigSchema = z
@@ -44,6 +45,7 @@ const ixDeployConfigSchema = z
44
45
  clamAVUrl: z.string().url(),
45
46
  vpcHttpProxy: z.string().url(),
46
47
  alarmSnsTopic: z.string().min(1),
48
+ tags: z.record(z.string(), z.string()),
47
49
  } satisfies Record<keyof ReturnType<typeof getEnvVars>, unknown>)
48
50
  .strip();
49
51
 
@@ -76,6 +78,7 @@ const nonIxDeployConfigSchema = z
76
78
  clamAVUrl: z.string(),
77
79
  vpcHttpProxy: z.string(),
78
80
  alarmSnsTopic: z.string(),
81
+ tags: z.record(z.string(), z.string()),
79
82
  } satisfies Record<keyof ReturnType<typeof getEnvVars>, unknown>)
80
83
  .strip();
81
84
 
@@ -0,0 +1,16 @@
1
+ import { IAspect, Tags } from "aws-cdk-lib";
2
+ import { IConstruct } from "constructs";
3
+
4
+ export class ConditionalTags implements IAspect {
5
+ constructor(
6
+ private getTags: (
7
+ node: IConstruct,
8
+ ) => Array<{ key: string; value: string }> | undefined | null,
9
+ ) {}
10
+
11
+ visit(node: IConstruct) {
12
+ this.getTags(node)?.forEach(({ key, value }) => {
13
+ Tags.of(node).add(key, value);
14
+ });
15
+ }
16
+ }
@@ -0,0 +1,2 @@
1
+ export { setupTags } from "./setupTags.js";
2
+ export { ConditionalTags } from "./ConditionalTags.js";
@@ -0,0 +1,55 @@
1
+ import { Aspects } from "aws-cdk-lib";
2
+ import { IConstruct } from "constructs";
3
+ import { Function } from "sst/constructs";
4
+ import { ConditionalTags } from "./ConditionalTags.js";
5
+ import deployConfig from "../../deployConfig.js";
6
+
7
+ export type ModifyTagsProps = {
8
+ node: IConstruct;
9
+ isLeafNode: boolean;
10
+ isRootNode: boolean;
11
+ currentTags: Array<{ key: string; value: string }>;
12
+ };
13
+
14
+ export type SetupTagsOptions = {
15
+ modifyTags?: (
16
+ props: ModifyTagsProps,
17
+ ) => Array<{ key: string; value: string }>;
18
+ };
19
+
20
+ export function setupTags(
21
+ scope: IConstruct,
22
+ { modifyTags }: SetupTagsOptions = {},
23
+ ) {
24
+ const conditionalTags = new ConditionalTags((node) => {
25
+ let tags = [];
26
+ const isLeafNode = node.node.children.length === 0;
27
+ const isRootNode = node === scope;
28
+
29
+ // Add tags from deploy config to all constructs
30
+ if (isRootNode) {
31
+ Object.entries(deployConfig.tags).forEach(([key, value]) => {
32
+ tags.push({ key, value });
33
+ });
34
+ }
35
+
36
+ // SST v2's live lambda feature means that the local machine that `sst dev` is run on may use AWS creds that were
37
+ // setup for a lambda. This triggers a false positive in GuardDuty, so we suppress that finding for any lambda that
38
+ // has live dev enabled.
39
+ if (node instanceof Function && node._isLiveDevEnabled) {
40
+ tags.push({ key: "guardduty-suppress", value: "true" });
41
+ }
42
+
43
+ tags = modifyTags
44
+ ? modifyTags({
45
+ node,
46
+ isLeafNode,
47
+ isRootNode,
48
+ currentTags: tags,
49
+ })
50
+ : tags;
51
+ return tags;
52
+ });
53
+
54
+ Aspects.of(scope).add(conditionalTags);
55
+ }