@stacksjs/ts-cloud-core 0.1.8 → 0.1.9

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 (225) hide show
  1. package/package.json +7 -6
  2. package/src/advanced-features.test.ts +465 -0
  3. package/src/aws/cloudformation.ts +421 -0
  4. package/src/aws/cloudfront.ts +158 -0
  5. package/src/aws/credentials.test.ts +132 -0
  6. package/src/aws/credentials.ts +545 -0
  7. package/src/aws/index.ts +87 -0
  8. package/src/aws/s3.test.ts +188 -0
  9. package/src/aws/s3.ts +1088 -0
  10. package/src/aws/signature.test.ts +670 -0
  11. package/src/aws/signature.ts +1155 -0
  12. package/src/backup/disaster-recovery.test.ts +726 -0
  13. package/src/backup/disaster-recovery.ts +500 -0
  14. package/src/backup/index.ts +34 -0
  15. package/src/backup/manager.test.ts +498 -0
  16. package/src/backup/manager.ts +432 -0
  17. package/src/cicd/circleci.ts +430 -0
  18. package/src/cicd/github-actions.ts +424 -0
  19. package/src/cicd/gitlab-ci.ts +255 -0
  20. package/src/cicd/index.ts +8 -0
  21. package/src/cli/history.ts +396 -0
  22. package/src/cli/index.ts +10 -0
  23. package/src/cli/progress.ts +458 -0
  24. package/src/cli/repl.ts +454 -0
  25. package/src/cli/suggestions.ts +327 -0
  26. package/src/cli/table.test.ts +319 -0
  27. package/src/cli/table.ts +332 -0
  28. package/src/cloudformation/builder.test.ts +327 -0
  29. package/src/cloudformation/builder.ts +378 -0
  30. package/src/cloudformation/builders/api-gateway.ts +449 -0
  31. package/src/cloudformation/builders/cache.ts +334 -0
  32. package/src/cloudformation/builders/cdn.ts +278 -0
  33. package/src/cloudformation/builders/compute.ts +485 -0
  34. package/src/cloudformation/builders/database.ts +392 -0
  35. package/src/cloudformation/builders/functions.ts +343 -0
  36. package/src/cloudformation/builders/messaging.ts +140 -0
  37. package/src/cloudformation/builders/monitoring.ts +300 -0
  38. package/src/cloudformation/builders/network.ts +264 -0
  39. package/src/cloudformation/builders/queue.ts +147 -0
  40. package/src/cloudformation/builders/security.ts +399 -0
  41. package/src/cloudformation/builders/storage.ts +285 -0
  42. package/src/cloudformation/index.ts +30 -0
  43. package/src/cloudformation/types.ts +173 -0
  44. package/src/compliance/aws-config.ts +543 -0
  45. package/src/compliance/cloudtrail.ts +376 -0
  46. package/src/compliance/compliance.test.ts +423 -0
  47. package/src/compliance/guardduty.ts +446 -0
  48. package/src/compliance/index.ts +66 -0
  49. package/src/compliance/security-hub.ts +456 -0
  50. package/src/containers/build-optimization.ts +416 -0
  51. package/src/containers/containers.test.ts +508 -0
  52. package/src/containers/image-scanning.ts +360 -0
  53. package/src/containers/index.ts +9 -0
  54. package/src/containers/registry.ts +293 -0
  55. package/src/containers/service-mesh.ts +520 -0
  56. package/src/database/database.test.ts +762 -0
  57. package/src/database/index.ts +9 -0
  58. package/src/database/migrations.ts +444 -0
  59. package/src/database/performance.ts +528 -0
  60. package/src/database/replicas.ts +534 -0
  61. package/src/database/users.ts +494 -0
  62. package/src/dependency-graph.ts +143 -0
  63. package/src/deployment/ab-testing.ts +582 -0
  64. package/src/deployment/blue-green.ts +452 -0
  65. package/src/deployment/canary.ts +500 -0
  66. package/src/deployment/deployment.test.ts +526 -0
  67. package/src/deployment/index.ts +61 -0
  68. package/src/deployment/progressive.ts +62 -0
  69. package/src/dns/dns.test.ts +641 -0
  70. package/src/dns/dnssec.ts +315 -0
  71. package/src/dns/index.ts +8 -0
  72. package/src/dns/resolver.ts +496 -0
  73. package/src/dns/routing.ts +593 -0
  74. package/src/email/advanced/analytics.ts +445 -0
  75. package/src/email/advanced/index.ts +11 -0
  76. package/src/email/advanced/rules.ts +465 -0
  77. package/src/email/advanced/scheduling.ts +352 -0
  78. package/src/email/advanced/search.ts +412 -0
  79. package/src/email/advanced/shared-mailboxes.ts +404 -0
  80. package/src/email/advanced/templates.ts +455 -0
  81. package/src/email/advanced/threading.ts +281 -0
  82. package/src/email/analytics.ts +467 -0
  83. package/src/email/bounce-handling.ts +425 -0
  84. package/src/email/email.test.ts +431 -0
  85. package/src/email/handlers/__tests__/inbound.test.ts +38 -0
  86. package/src/email/handlers/__tests__/outbound.test.ts +37 -0
  87. package/src/email/handlers/converter.ts +227 -0
  88. package/src/email/handlers/feedback.ts +228 -0
  89. package/src/email/handlers/inbound.ts +169 -0
  90. package/src/email/handlers/outbound.ts +178 -0
  91. package/src/email/index.ts +15 -0
  92. package/src/email/reputation.ts +303 -0
  93. package/src/email/templates.ts +352 -0
  94. package/src/errors/index.test.ts +434 -0
  95. package/src/errors/index.ts +416 -0
  96. package/src/health-checks/index.ts +40 -0
  97. package/src/index.ts +360 -0
  98. package/src/intrinsic-functions.ts +118 -0
  99. package/src/lambda/concurrency.ts +330 -0
  100. package/src/lambda/destinations.ts +345 -0
  101. package/src/lambda/dlq.ts +425 -0
  102. package/src/lambda/index.ts +11 -0
  103. package/src/lambda/lambda.test.ts +840 -0
  104. package/src/lambda/layers.ts +263 -0
  105. package/src/lambda/versions.ts +376 -0
  106. package/src/lambda/vpc.ts +399 -0
  107. package/src/local/config.ts +114 -0
  108. package/src/local/index.ts +6 -0
  109. package/src/local/mock-aws.ts +351 -0
  110. package/src/modules/ai.ts +340 -0
  111. package/src/modules/api.ts +478 -0
  112. package/src/modules/auth.ts +805 -0
  113. package/src/modules/cache.ts +417 -0
  114. package/src/modules/cdn.ts +1062 -0
  115. package/src/modules/communication.ts +1094 -0
  116. package/src/modules/compute.ts +3348 -0
  117. package/src/modules/database.ts +554 -0
  118. package/src/modules/deployment.ts +1079 -0
  119. package/src/modules/dns.ts +337 -0
  120. package/src/modules/email.ts +1538 -0
  121. package/src/modules/filesystem.ts +515 -0
  122. package/src/modules/index.ts +32 -0
  123. package/src/modules/messaging.ts +486 -0
  124. package/src/modules/monitoring.ts +2086 -0
  125. package/src/modules/network.ts +664 -0
  126. package/src/modules/parameter-store.ts +325 -0
  127. package/src/modules/permissions.ts +1081 -0
  128. package/src/modules/phone.ts +494 -0
  129. package/src/modules/queue.ts +1260 -0
  130. package/src/modules/redirects.ts +464 -0
  131. package/src/modules/registry.ts +699 -0
  132. package/src/modules/search.ts +401 -0
  133. package/src/modules/secrets.ts +416 -0
  134. package/src/modules/security.ts +731 -0
  135. package/src/modules/sms.ts +389 -0
  136. package/src/modules/storage.ts +1120 -0
  137. package/src/modules/workflow.ts +680 -0
  138. package/src/multi-account/config.ts +521 -0
  139. package/src/multi-account/index.ts +7 -0
  140. package/src/multi-account/manager.ts +427 -0
  141. package/src/multi-region/cross-region.ts +410 -0
  142. package/src/multi-region/index.ts +8 -0
  143. package/src/multi-region/manager.ts +483 -0
  144. package/src/multi-region/regions.ts +435 -0
  145. package/src/network-security/index.ts +48 -0
  146. package/src/observability/index.ts +9 -0
  147. package/src/observability/logs.ts +522 -0
  148. package/src/observability/metrics.ts +460 -0
  149. package/src/observability/observability.test.ts +782 -0
  150. package/src/observability/synthetics.ts +568 -0
  151. package/src/observability/xray.ts +358 -0
  152. package/src/phone/advanced/analytics.ts +349 -0
  153. package/src/phone/advanced/callbacks.ts +428 -0
  154. package/src/phone/advanced/index.ts +8 -0
  155. package/src/phone/advanced/ivr-builder.ts +504 -0
  156. package/src/phone/advanced/recording.ts +310 -0
  157. package/src/phone/handlers/__tests__/incoming-call.test.ts +40 -0
  158. package/src/phone/handlers/incoming-call.ts +117 -0
  159. package/src/phone/handlers/missed-call.ts +116 -0
  160. package/src/phone/handlers/voicemail.ts +179 -0
  161. package/src/phone/index.ts +9 -0
  162. package/src/presets/api-backend.ts +134 -0
  163. package/src/presets/data-pipeline.ts +204 -0
  164. package/src/presets/extend.test.ts +295 -0
  165. package/src/presets/extend.ts +297 -0
  166. package/src/presets/fullstack-app.ts +144 -0
  167. package/src/presets/index.ts +27 -0
  168. package/src/presets/jamstack.ts +135 -0
  169. package/src/presets/microservices.ts +167 -0
  170. package/src/presets/ml-api.ts +208 -0
  171. package/src/presets/nodejs-server.ts +104 -0
  172. package/src/presets/nodejs-serverless.ts +114 -0
  173. package/src/presets/realtime-app.ts +184 -0
  174. package/src/presets/static-site.ts +64 -0
  175. package/src/presets/traditional-web-app.ts +339 -0
  176. package/src/presets/wordpress.ts +138 -0
  177. package/src/preview/github.test.ts +249 -0
  178. package/src/preview/github.ts +297 -0
  179. package/src/preview/index.ts +37 -0
  180. package/src/preview/manager.test.ts +440 -0
  181. package/src/preview/manager.ts +326 -0
  182. package/src/preview/notifications.test.ts +582 -0
  183. package/src/preview/notifications.ts +341 -0
  184. package/src/queue/batch-processing.ts +402 -0
  185. package/src/queue/dlq-monitoring.ts +402 -0
  186. package/src/queue/fifo.ts +342 -0
  187. package/src/queue/index.ts +9 -0
  188. package/src/queue/management.ts +428 -0
  189. package/src/queue/queue.test.ts +429 -0
  190. package/src/resource-mgmt/index.ts +39 -0
  191. package/src/resource-naming.ts +62 -0
  192. package/src/s3/index.ts +523 -0
  193. package/src/schema/cloud-config.schema.json +554 -0
  194. package/src/schema/index.ts +68 -0
  195. package/src/security/certificate-manager.ts +492 -0
  196. package/src/security/index.ts +9 -0
  197. package/src/security/scanning.ts +545 -0
  198. package/src/security/secrets-manager.ts +476 -0
  199. package/src/security/secrets-rotation.ts +456 -0
  200. package/src/security/security.test.ts +738 -0
  201. package/src/sms/advanced/ab-testing.ts +389 -0
  202. package/src/sms/advanced/analytics.ts +336 -0
  203. package/src/sms/advanced/campaigns.ts +523 -0
  204. package/src/sms/advanced/chatbot.ts +224 -0
  205. package/src/sms/advanced/index.ts +10 -0
  206. package/src/sms/advanced/link-tracking.ts +248 -0
  207. package/src/sms/advanced/mms.ts +308 -0
  208. package/src/sms/handlers/__tests__/send.test.ts +40 -0
  209. package/src/sms/handlers/delivery-status.ts +133 -0
  210. package/src/sms/handlers/receive.ts +162 -0
  211. package/src/sms/handlers/send.ts +174 -0
  212. package/src/sms/index.ts +9 -0
  213. package/src/stack-diff.ts +389 -0
  214. package/src/static-site/index.ts +85 -0
  215. package/src/template-builder.ts +110 -0
  216. package/src/template-validator.ts +574 -0
  217. package/src/utils/cache.ts +291 -0
  218. package/src/utils/diff.ts +269 -0
  219. package/src/utils/hash.ts +227 -0
  220. package/src/utils/index.ts +8 -0
  221. package/src/utils/parallel.ts +294 -0
  222. package/src/validators/credentials.test.ts +274 -0
  223. package/src/validators/credentials.ts +233 -0
  224. package/src/validators/quotas.test.ts +434 -0
  225. package/src/validators/quotas.ts +217 -0
@@ -0,0 +1,2086 @@
1
+ import type {
2
+ CloudWatchAlarm,
3
+ CloudWatchDashboard,
4
+ CloudWatchLogGroup,
5
+ CloudWatchLogStream,
6
+ CloudWatchMetricFilter,
7
+ CloudWatchCompositeAlarm,
8
+ } from '@stacksjs/ts-cloud-aws-types'
9
+ import type { EnvironmentType } from '@stacksjs/ts-cloud-types'
10
+ import { Fn } from '../intrinsic-functions'
11
+ import { generateLogicalId, generateResourceName } from '../resource-naming'
12
+
13
+ export interface AlarmOptions {
14
+ slug: string
15
+ environment: EnvironmentType
16
+ alarmName?: string
17
+ metricName: string
18
+ namespace: string
19
+ statistic?: 'Average' | 'Sum' | 'Minimum' | 'Maximum' | 'SampleCount'
20
+ period?: number
21
+ evaluationPeriods?: number
22
+ threshold: number
23
+ comparisonOperator: 'GreaterThanThreshold' | 'GreaterThanOrEqualToThreshold' | 'LessThanThreshold' | 'LessThanOrEqualToThreshold'
24
+ treatMissingData?: 'breaching' | 'notBreaching' | 'ignore' | 'missing'
25
+ actionsEnabled?: boolean
26
+ alarmActions?: string[]
27
+ okActions?: string[]
28
+ insufficientDataActions?: string[]
29
+ dimensions?: Record<string, string>
30
+ unit?: string
31
+ datapointsToAlarm?: number
32
+ }
33
+
34
+ export interface DashboardOptions {
35
+ slug: string
36
+ environment: EnvironmentType
37
+ dashboardName?: string
38
+ widgets: DashboardWidget[]
39
+ }
40
+
41
+ export interface DashboardWidget {
42
+ type: 'metric' | 'log' | 'text' | 'alarm'
43
+ x: number
44
+ y: number
45
+ width: number
46
+ height: number
47
+ properties: Record<string, unknown>
48
+ }
49
+
50
+ export interface LogGroupOptions {
51
+ slug: string
52
+ environment: EnvironmentType
53
+ logGroupName?: string
54
+ retentionInDays?: number
55
+ kmsKeyId?: string
56
+ }
57
+
58
+ export interface LogStreamOptions {
59
+ slug: string
60
+ environment: EnvironmentType
61
+ logStreamName?: string
62
+ }
63
+
64
+ export interface MetricFilterOptions {
65
+ slug: string
66
+ environment: EnvironmentType
67
+ filterName?: string
68
+ filterPattern: string
69
+ metricTransformations: MetricTransformation[]
70
+ }
71
+
72
+ export interface MetricTransformation {
73
+ metricName: string
74
+ metricNamespace: string
75
+ metricValue: string
76
+ defaultValue?: number
77
+ unit?: string
78
+ }
79
+
80
+ export interface CompositeAlarmOptions {
81
+ slug: string
82
+ environment: EnvironmentType
83
+ alarmName?: string
84
+ alarmRule: string
85
+ actionsEnabled?: boolean
86
+ alarmActions?: string[]
87
+ okActions?: string[]
88
+ insufficientDataActions?: string[]
89
+ }
90
+
91
+ /**
92
+ * Monitoring Module - CloudWatch
93
+ * Provides clean API for alarms, dashboards, logs, and metrics
94
+ */
95
+ export class Monitoring {
96
+ /**
97
+ * Create a CloudWatch alarm
98
+ */
99
+ static createAlarm(options: AlarmOptions): {
100
+ alarm: CloudWatchAlarm
101
+ logicalId: string
102
+ } {
103
+ const {
104
+ slug,
105
+ environment,
106
+ alarmName,
107
+ metricName,
108
+ namespace,
109
+ statistic = 'Average',
110
+ period = 300,
111
+ evaluationPeriods = 1,
112
+ threshold,
113
+ comparisonOperator,
114
+ treatMissingData = 'notBreaching',
115
+ actionsEnabled = true,
116
+ alarmActions,
117
+ okActions,
118
+ insufficientDataActions,
119
+ dimensions,
120
+ unit,
121
+ datapointsToAlarm,
122
+ } = options
123
+
124
+ const resourceName = alarmName || generateResourceName({
125
+ slug,
126
+ environment,
127
+ resourceType: 'alarm',
128
+ })
129
+
130
+ const logicalId = generateLogicalId(resourceName)
131
+
132
+ const alarm: CloudWatchAlarm = {
133
+ Type: 'AWS::CloudWatch::Alarm',
134
+ Properties: {
135
+ AlarmName: resourceName,
136
+ MetricName: metricName,
137
+ Namespace: namespace,
138
+ Statistic: statistic,
139
+ Period: period,
140
+ EvaluationPeriods: evaluationPeriods,
141
+ Threshold: threshold,
142
+ ComparisonOperator: comparisonOperator,
143
+ TreatMissingData: treatMissingData,
144
+ ActionsEnabled: actionsEnabled,
145
+ },
146
+ }
147
+
148
+ if (alarmActions && alarmActions.length > 0) {
149
+ alarm.Properties!.AlarmActions = alarmActions
150
+ }
151
+
152
+ if (okActions && okActions.length > 0) {
153
+ alarm.Properties!.OKActions = okActions
154
+ }
155
+
156
+ if (insufficientDataActions && insufficientDataActions.length > 0) {
157
+ alarm.Properties!.InsufficientDataActions = insufficientDataActions
158
+ }
159
+
160
+ if (dimensions) {
161
+ alarm.Properties!.Dimensions = Object.entries(dimensions).map(([name, value]) => ({
162
+ Name: name,
163
+ Value: value,
164
+ }))
165
+ }
166
+
167
+ if (unit) {
168
+ alarm.Properties!.Unit = unit
169
+ }
170
+
171
+ if (datapointsToAlarm !== undefined) {
172
+ alarm.Properties!.DatapointsToAlarm = datapointsToAlarm
173
+ }
174
+
175
+ return { alarm, logicalId }
176
+ }
177
+
178
+ /**
179
+ * Create a composite alarm (combines multiple alarms)
180
+ */
181
+ static createCompositeAlarm(options: CompositeAlarmOptions): {
182
+ alarm: CloudWatchCompositeAlarm
183
+ logicalId: string
184
+ } {
185
+ const {
186
+ slug,
187
+ environment,
188
+ alarmName,
189
+ alarmRule,
190
+ actionsEnabled = true,
191
+ alarmActions,
192
+ okActions,
193
+ insufficientDataActions,
194
+ } = options
195
+
196
+ const resourceName = alarmName || generateResourceName({
197
+ slug,
198
+ environment,
199
+ resourceType: 'composite-alarm',
200
+ })
201
+
202
+ const logicalId = generateLogicalId(resourceName)
203
+
204
+ const alarm: CloudWatchCompositeAlarm = {
205
+ Type: 'AWS::CloudWatch::CompositeAlarm',
206
+ Properties: {
207
+ AlarmName: resourceName,
208
+ AlarmRule: alarmRule,
209
+ ActionsEnabled: actionsEnabled,
210
+ },
211
+ }
212
+
213
+ if (alarmActions && alarmActions.length > 0) {
214
+ alarm.Properties!.AlarmActions = alarmActions
215
+ }
216
+
217
+ if (okActions && okActions.length > 0) {
218
+ alarm.Properties!.OKActions = okActions
219
+ }
220
+
221
+ if (insufficientDataActions && insufficientDataActions.length > 0) {
222
+ alarm.Properties!.InsufficientDataActions = insufficientDataActions
223
+ }
224
+
225
+ return { alarm, logicalId }
226
+ }
227
+
228
+ /**
229
+ * Create a CloudWatch dashboard
230
+ */
231
+ static createDashboard(options: DashboardOptions): {
232
+ dashboard: CloudWatchDashboard
233
+ logicalId: string
234
+ } {
235
+ const {
236
+ slug,
237
+ environment,
238
+ dashboardName,
239
+ widgets,
240
+ } = options
241
+
242
+ const resourceName = dashboardName || generateResourceName({
243
+ slug,
244
+ environment,
245
+ resourceType: 'dashboard',
246
+ })
247
+
248
+ const logicalId = generateLogicalId(resourceName)
249
+
250
+ const dashboardBody = {
251
+ widgets: widgets.map(widget => ({
252
+ type: widget.type,
253
+ x: widget.x,
254
+ y: widget.y,
255
+ width: widget.width,
256
+ height: widget.height,
257
+ properties: widget.properties,
258
+ })),
259
+ }
260
+
261
+ const dashboard: CloudWatchDashboard = {
262
+ Type: 'AWS::CloudWatch::Dashboard',
263
+ Properties: {
264
+ DashboardName: resourceName,
265
+ DashboardBody: JSON.stringify(dashboardBody),
266
+ },
267
+ }
268
+
269
+ return { dashboard, logicalId }
270
+ }
271
+
272
+ /**
273
+ * Create a CloudWatch log group
274
+ */
275
+ static createLogGroup(options: LogGroupOptions): {
276
+ logGroup: CloudWatchLogGroup
277
+ logicalId: string
278
+ } {
279
+ const {
280
+ slug,
281
+ environment,
282
+ logGroupName,
283
+ retentionInDays,
284
+ kmsKeyId,
285
+ } = options
286
+
287
+ const resourceName = logGroupName || generateResourceName({
288
+ slug,
289
+ environment,
290
+ resourceType: 'log-group',
291
+ })
292
+
293
+ const logicalId = generateLogicalId(resourceName)
294
+
295
+ const logGroup: CloudWatchLogGroup = {
296
+ Type: 'AWS::Logs::LogGroup',
297
+ Properties: {
298
+ LogGroupName: resourceName,
299
+ },
300
+ }
301
+
302
+ if (retentionInDays !== undefined) {
303
+ logGroup.Properties!.RetentionInDays = retentionInDays as 1 | 3 | 5 | 7 | 14 | 30 | 60 | 90 | 120 | 150 | 180 | 365 | 400 | 545 | 731 | 1827 | 3653
304
+ }
305
+
306
+ if (kmsKeyId) {
307
+ logGroup.Properties!.KmsKeyId = kmsKeyId
308
+ }
309
+
310
+ return { logGroup, logicalId }
311
+ }
312
+
313
+ /**
314
+ * Create a CloudWatch log stream
315
+ */
316
+ static createLogStream(
317
+ logGroupLogicalId: string,
318
+ options: LogStreamOptions,
319
+ ): {
320
+ logStream: CloudWatchLogStream
321
+ logicalId: string
322
+ } {
323
+ const {
324
+ slug,
325
+ environment,
326
+ logStreamName,
327
+ } = options
328
+
329
+ const resourceName = logStreamName || generateResourceName({
330
+ slug,
331
+ environment,
332
+ resourceType: 'log-stream',
333
+ })
334
+
335
+ const logicalId = generateLogicalId(resourceName)
336
+
337
+ const logStream: CloudWatchLogStream = {
338
+ Type: 'AWS::Logs::LogStream',
339
+ Properties: {
340
+ LogGroupName: Fn.Ref(logGroupLogicalId) as unknown as string,
341
+ LogStreamName: resourceName,
342
+ },
343
+ }
344
+
345
+ return { logStream, logicalId }
346
+ }
347
+
348
+ /**
349
+ * Create a metric filter for log group
350
+ */
351
+ static createMetricFilter(
352
+ logGroupLogicalId: string,
353
+ options: MetricFilterOptions,
354
+ ): {
355
+ metricFilter: CloudWatchMetricFilter
356
+ logicalId: string
357
+ } {
358
+ const {
359
+ slug,
360
+ environment,
361
+ filterName,
362
+ filterPattern,
363
+ metricTransformations,
364
+ } = options
365
+
366
+ const resourceName = filterName || generateResourceName({
367
+ slug,
368
+ environment,
369
+ resourceType: 'metric-filter',
370
+ })
371
+
372
+ const logicalId = generateLogicalId(resourceName)
373
+
374
+ const metricFilter: CloudWatchMetricFilter = {
375
+ Type: 'AWS::Logs::MetricFilter',
376
+ Properties: {
377
+ LogGroupName: Fn.Ref(logGroupLogicalId) as unknown as string,
378
+ FilterPattern: filterPattern,
379
+ MetricTransformations: metricTransformations.map(t => ({
380
+ MetricName: t.metricName,
381
+ MetricNamespace: t.metricNamespace,
382
+ MetricValue: t.metricValue,
383
+ DefaultValue: t.defaultValue,
384
+ Unit: t.unit,
385
+ })),
386
+ },
387
+ }
388
+
389
+ return { metricFilter, logicalId }
390
+ }
391
+
392
+ /**
393
+ * Common alarm configurations
394
+ */
395
+ static readonly AlarmTypes = {
396
+ /**
397
+ * High CPU utilization alarm
398
+ */
399
+ highCpu: (
400
+ slug: string,
401
+ environment: EnvironmentType,
402
+ resourceId: string,
403
+ threshold: number = 80,
404
+ snsTopicArn?: string,
405
+ ): AlarmOptions => ({
406
+ slug,
407
+ environment,
408
+ alarmName: `${slug}-${environment}-high-cpu`,
409
+ metricName: 'CPUUtilization',
410
+ namespace: 'AWS/EC2',
411
+ statistic: 'Average',
412
+ period: 300,
413
+ evaluationPeriods: 2,
414
+ threshold,
415
+ comparisonOperator: 'GreaterThanThreshold',
416
+ dimensions: { InstanceId: resourceId },
417
+ alarmActions: snsTopicArn ? [snsTopicArn] : undefined,
418
+ }),
419
+
420
+ /**
421
+ * High memory utilization alarm
422
+ */
423
+ highMemory: (
424
+ slug: string,
425
+ environment: EnvironmentType,
426
+ resourceId: string,
427
+ threshold: number = 80,
428
+ snsTopicArn?: string,
429
+ ): AlarmOptions => ({
430
+ slug,
431
+ environment,
432
+ alarmName: `${slug}-${environment}-high-memory`,
433
+ metricName: 'MemoryUtilization',
434
+ namespace: 'System/Linux',
435
+ statistic: 'Average',
436
+ period: 300,
437
+ evaluationPeriods: 2,
438
+ threshold,
439
+ comparisonOperator: 'GreaterThanThreshold',
440
+ dimensions: { InstanceId: resourceId },
441
+ alarmActions: snsTopicArn ? [snsTopicArn] : undefined,
442
+ }),
443
+
444
+ /**
445
+ * High disk utilization alarm
446
+ */
447
+ highDisk: (
448
+ slug: string,
449
+ environment: EnvironmentType,
450
+ resourceId: string,
451
+ threshold: number = 80,
452
+ snsTopicArn?: string,
453
+ ): AlarmOptions => ({
454
+ slug,
455
+ environment,
456
+ alarmName: `${slug}-${environment}-high-disk`,
457
+ metricName: 'DiskSpaceUtilization',
458
+ namespace: 'System/Linux',
459
+ statistic: 'Average',
460
+ period: 300,
461
+ evaluationPeriods: 2,
462
+ threshold,
463
+ comparisonOperator: 'GreaterThanThreshold',
464
+ dimensions: { InstanceId: resourceId, Filesystem: '/dev/xvda1', MountPath: '/' },
465
+ alarmActions: snsTopicArn ? [snsTopicArn] : undefined,
466
+ }),
467
+
468
+ /**
469
+ * Lambda error rate alarm
470
+ */
471
+ lambdaErrors: (
472
+ slug: string,
473
+ environment: EnvironmentType,
474
+ functionName: string,
475
+ threshold: number = 5,
476
+ snsTopicArn?: string,
477
+ ): AlarmOptions => ({
478
+ slug,
479
+ environment,
480
+ alarmName: `${slug}-${environment}-lambda-errors`,
481
+ metricName: 'Errors',
482
+ namespace: 'AWS/Lambda',
483
+ statistic: 'Sum',
484
+ period: 300,
485
+ evaluationPeriods: 1,
486
+ threshold,
487
+ comparisonOperator: 'GreaterThanThreshold',
488
+ dimensions: { FunctionName: functionName },
489
+ alarmActions: snsTopicArn ? [snsTopicArn] : undefined,
490
+ }),
491
+
492
+ /**
493
+ * Lambda throttles alarm
494
+ */
495
+ lambdaThrottles: (
496
+ slug: string,
497
+ environment: EnvironmentType,
498
+ functionName: string,
499
+ threshold: number = 10,
500
+ snsTopicArn?: string,
501
+ ): AlarmOptions => ({
502
+ slug,
503
+ environment,
504
+ alarmName: `${slug}-${environment}-lambda-throttles`,
505
+ metricName: 'Throttles',
506
+ namespace: 'AWS/Lambda',
507
+ statistic: 'Sum',
508
+ period: 300,
509
+ evaluationPeriods: 1,
510
+ threshold,
511
+ comparisonOperator: 'GreaterThanThreshold',
512
+ dimensions: { FunctionName: functionName },
513
+ alarmActions: snsTopicArn ? [snsTopicArn] : undefined,
514
+ }),
515
+
516
+ /**
517
+ * API Gateway 5xx errors alarm
518
+ */
519
+ apiGateway5xxErrors: (
520
+ slug: string,
521
+ environment: EnvironmentType,
522
+ apiName: string,
523
+ threshold: number = 10,
524
+ snsTopicArn?: string,
525
+ ): AlarmOptions => ({
526
+ slug,
527
+ environment,
528
+ alarmName: `${slug}-${environment}-api-5xx-errors`,
529
+ metricName: '5XXError',
530
+ namespace: 'AWS/ApiGateway',
531
+ statistic: 'Sum',
532
+ period: 300,
533
+ evaluationPeriods: 1,
534
+ threshold,
535
+ comparisonOperator: 'GreaterThanThreshold',
536
+ dimensions: { ApiName: apiName },
537
+ alarmActions: snsTopicArn ? [snsTopicArn] : undefined,
538
+ }),
539
+
540
+ /**
541
+ * API Gateway 4xx errors alarm
542
+ */
543
+ apiGateway4xxErrors: (
544
+ slug: string,
545
+ environment: EnvironmentType,
546
+ apiName: string,
547
+ threshold: number = 50,
548
+ snsTopicArn?: string,
549
+ ): AlarmOptions => ({
550
+ slug,
551
+ environment,
552
+ alarmName: `${slug}-${environment}-api-4xx-errors`,
553
+ metricName: '4XXError',
554
+ namespace: 'AWS/ApiGateway',
555
+ statistic: 'Sum',
556
+ period: 300,
557
+ evaluationPeriods: 2,
558
+ threshold,
559
+ comparisonOperator: 'GreaterThanThreshold',
560
+ dimensions: { ApiName: apiName },
561
+ alarmActions: snsTopicArn ? [snsTopicArn] : undefined,
562
+ }),
563
+
564
+ /**
565
+ * DynamoDB throttled requests alarm
566
+ */
567
+ dynamoDBThrottles: (
568
+ slug: string,
569
+ environment: EnvironmentType,
570
+ tableName: string,
571
+ threshold: number = 5,
572
+ snsTopicArn?: string,
573
+ ): AlarmOptions => ({
574
+ slug,
575
+ environment,
576
+ alarmName: `${slug}-${environment}-dynamodb-throttles`,
577
+ metricName: 'UserErrors',
578
+ namespace: 'AWS/DynamoDB',
579
+ statistic: 'Sum',
580
+ period: 300,
581
+ evaluationPeriods: 1,
582
+ threshold,
583
+ comparisonOperator: 'GreaterThanThreshold',
584
+ dimensions: { TableName: tableName },
585
+ alarmActions: snsTopicArn ? [snsTopicArn] : undefined,
586
+ }),
587
+
588
+ /**
589
+ * RDS CPU alarm
590
+ */
591
+ rdsCpu: (
592
+ slug: string,
593
+ environment: EnvironmentType,
594
+ dbInstanceId: string,
595
+ threshold: number = 80,
596
+ snsTopicArn?: string,
597
+ ): AlarmOptions => ({
598
+ slug,
599
+ environment,
600
+ alarmName: `${slug}-${environment}-rds-cpu`,
601
+ metricName: 'CPUUtilization',
602
+ namespace: 'AWS/RDS',
603
+ statistic: 'Average',
604
+ period: 300,
605
+ evaluationPeriods: 2,
606
+ threshold,
607
+ comparisonOperator: 'GreaterThanThreshold',
608
+ dimensions: { DBInstanceIdentifier: dbInstanceId },
609
+ alarmActions: snsTopicArn ? [snsTopicArn] : undefined,
610
+ }),
611
+
612
+ /**
613
+ * RDS free storage alarm
614
+ */
615
+ rdsFreeStorage: (
616
+ slug: string,
617
+ environment: EnvironmentType,
618
+ dbInstanceId: string,
619
+ threshold: number = 5368709120, // 5GB in bytes
620
+ snsTopicArn?: string,
621
+ ): AlarmOptions => ({
622
+ slug,
623
+ environment,
624
+ alarmName: `${slug}-${environment}-rds-storage`,
625
+ metricName: 'FreeStorageSpace',
626
+ namespace: 'AWS/RDS',
627
+ statistic: 'Average',
628
+ period: 300,
629
+ evaluationPeriods: 1,
630
+ threshold,
631
+ comparisonOperator: 'LessThanThreshold',
632
+ dimensions: { DBInstanceIdentifier: dbInstanceId },
633
+ alarmActions: snsTopicArn ? [snsTopicArn] : undefined,
634
+ unit: 'Bytes',
635
+ }),
636
+
637
+ /**
638
+ * SQS queue depth alarm
639
+ */
640
+ sqsQueueDepth: (
641
+ slug: string,
642
+ environment: EnvironmentType,
643
+ queueName: string,
644
+ threshold: number = 100,
645
+ snsTopicArn?: string,
646
+ ): AlarmOptions => ({
647
+ slug,
648
+ environment,
649
+ alarmName: `${slug}-${environment}-sqs-depth`,
650
+ metricName: 'ApproximateNumberOfMessagesVisible',
651
+ namespace: 'AWS/SQS',
652
+ statistic: 'Average',
653
+ period: 300,
654
+ evaluationPeriods: 2,
655
+ threshold,
656
+ comparisonOperator: 'GreaterThanThreshold',
657
+ dimensions: { QueueName: queueName },
658
+ alarmActions: snsTopicArn ? [snsTopicArn] : undefined,
659
+ }),
660
+
661
+ /**
662
+ * ALB target unhealthy alarm
663
+ */
664
+ albUnhealthyTargets: (
665
+ slug: string,
666
+ environment: EnvironmentType,
667
+ loadBalancer: string,
668
+ targetGroup: string,
669
+ threshold: number = 1,
670
+ snsTopicArn?: string,
671
+ ): AlarmOptions => ({
672
+ slug,
673
+ environment,
674
+ alarmName: `${slug}-${environment}-alb-unhealthy`,
675
+ metricName: 'UnHealthyHostCount',
676
+ namespace: 'AWS/ApplicationELB',
677
+ statistic: 'Average',
678
+ period: 300,
679
+ evaluationPeriods: 2,
680
+ threshold,
681
+ comparisonOperator: 'GreaterThanThreshold',
682
+ dimensions: { LoadBalancer: loadBalancer, TargetGroup: targetGroup },
683
+ alarmActions: snsTopicArn ? [snsTopicArn] : undefined,
684
+ }),
685
+
686
+ /**
687
+ * SES bounce rate alarm
688
+ */
689
+ sesBounceRate: (
690
+ slug: string,
691
+ environment: EnvironmentType,
692
+ threshold: number = 5,
693
+ snsTopicArn?: string,
694
+ ): AlarmOptions => ({
695
+ slug,
696
+ environment,
697
+ alarmName: `${slug}-${environment}-ses-bounce-rate`,
698
+ metricName: 'Reputation.BounceRate',
699
+ namespace: 'AWS/SES',
700
+ statistic: 'Average',
701
+ period: 3600,
702
+ evaluationPeriods: 1,
703
+ threshold: threshold / 100, // Convert percentage to decimal
704
+ comparisonOperator: 'GreaterThanThreshold',
705
+ alarmActions: snsTopicArn ? [snsTopicArn] : undefined,
706
+ }),
707
+
708
+ /**
709
+ * SES complaint rate alarm
710
+ */
711
+ sesComplaintRate: (
712
+ slug: string,
713
+ environment: EnvironmentType,
714
+ threshold: number = 0.1,
715
+ snsTopicArn?: string,
716
+ ): AlarmOptions => ({
717
+ slug,
718
+ environment,
719
+ alarmName: `${slug}-${environment}-ses-complaint-rate`,
720
+ metricName: 'Reputation.ComplaintRate',
721
+ namespace: 'AWS/SES',
722
+ statistic: 'Average',
723
+ period: 3600,
724
+ evaluationPeriods: 1,
725
+ threshold: threshold / 100,
726
+ comparisonOperator: 'GreaterThanThreshold',
727
+ alarmActions: snsTopicArn ? [snsTopicArn] : undefined,
728
+ }),
729
+
730
+ /**
731
+ * Pinpoint SMS delivery failure alarm
732
+ */
733
+ smsDeliveryFailure: (
734
+ slug: string,
735
+ environment: EnvironmentType,
736
+ applicationId: string,
737
+ threshold: number = 10,
738
+ snsTopicArn?: string,
739
+ ): AlarmOptions => ({
740
+ slug,
741
+ environment,
742
+ alarmName: `${slug}-${environment}-sms-delivery-failure`,
743
+ metricName: 'DirectSendMessagePermanentFailure',
744
+ namespace: 'AWS/Pinpoint',
745
+ statistic: 'Sum',
746
+ period: 300,
747
+ evaluationPeriods: 1,
748
+ threshold,
749
+ comparisonOperator: 'GreaterThanThreshold',
750
+ dimensions: { ApplicationId: applicationId },
751
+ alarmActions: snsTopicArn ? [snsTopicArn] : undefined,
752
+ }),
753
+
754
+ /**
755
+ * Pinpoint SMS spend alarm
756
+ */
757
+ smsSpendLimit: (
758
+ slug: string,
759
+ environment: EnvironmentType,
760
+ threshold: number = 100,
761
+ snsTopicArn?: string,
762
+ ): AlarmOptions => ({
763
+ slug,
764
+ environment,
765
+ alarmName: `${slug}-${environment}-sms-spend-limit`,
766
+ metricName: 'DirectSendMessageSpend',
767
+ namespace: 'AWS/Pinpoint',
768
+ statistic: 'Sum',
769
+ period: 86400, // Daily
770
+ evaluationPeriods: 1,
771
+ threshold,
772
+ comparisonOperator: 'GreaterThanThreshold',
773
+ alarmActions: snsTopicArn ? [snsTopicArn] : undefined,
774
+ }),
775
+
776
+ /**
777
+ * Connect missed calls alarm
778
+ */
779
+ connectMissedCalls: (
780
+ slug: string,
781
+ environment: EnvironmentType,
782
+ instanceId: string,
783
+ threshold: number = 5,
784
+ snsTopicArn?: string,
785
+ ): AlarmOptions => ({
786
+ slug,
787
+ environment,
788
+ alarmName: `${slug}-${environment}-connect-missed-calls`,
789
+ metricName: 'MissedCalls',
790
+ namespace: 'AWS/Connect',
791
+ statistic: 'Sum',
792
+ period: 3600,
793
+ evaluationPeriods: 1,
794
+ threshold,
795
+ comparisonOperator: 'GreaterThanThreshold',
796
+ dimensions: { InstanceId: instanceId },
797
+ alarmActions: snsTopicArn ? [snsTopicArn] : undefined,
798
+ }),
799
+ } as const
800
+
801
+ /**
802
+ * Common dashboard widgets
803
+ */
804
+ static readonly DashboardWidgets = {
805
+ /**
806
+ * Metric widget
807
+ */
808
+ metric: (
809
+ x: number,
810
+ y: number,
811
+ width: number,
812
+ height: number,
813
+ metrics: Array<[string, string, Record<string, string>?]>,
814
+ title?: string,
815
+ ): DashboardWidget => ({
816
+ type: 'metric',
817
+ x,
818
+ y,
819
+ width,
820
+ height,
821
+ properties: {
822
+ metrics,
823
+ title: title || 'Metrics',
824
+ region: 'us-east-1',
825
+ period: 300,
826
+ },
827
+ }),
828
+
829
+ /**
830
+ * Text widget
831
+ */
832
+ text: (
833
+ x: number,
834
+ y: number,
835
+ width: number,
836
+ height: number,
837
+ markdown: string,
838
+ ): DashboardWidget => ({
839
+ type: 'text',
840
+ x,
841
+ y,
842
+ width,
843
+ height,
844
+ properties: {
845
+ markdown,
846
+ },
847
+ }),
848
+
849
+ /**
850
+ * Log widget
851
+ */
852
+ log: (
853
+ x: number,
854
+ y: number,
855
+ width: number,
856
+ height: number,
857
+ logGroupNames: string[],
858
+ title?: string,
859
+ ): DashboardWidget => ({
860
+ type: 'log',
861
+ x,
862
+ y,
863
+ width,
864
+ height,
865
+ properties: {
866
+ query: `SOURCE '${logGroupNames.join("' | SOURCE '")}'
867
+ | fields @timestamp, @message
868
+ | sort @timestamp desc
869
+ | limit 20`,
870
+ region: 'us-east-1',
871
+ title: title || 'Logs',
872
+ },
873
+ }),
874
+ } as const
875
+
876
+ /**
877
+ * Common log retention periods
878
+ */
879
+ static readonly RetentionPeriods = {
880
+ ONE_DAY: 1,
881
+ THREE_DAYS: 3,
882
+ FIVE_DAYS: 5,
883
+ ONE_WEEK: 7,
884
+ TWO_WEEKS: 14,
885
+ ONE_MONTH: 30,
886
+ TWO_MONTHS: 60,
887
+ THREE_MONTHS: 90,
888
+ FOUR_MONTHS: 120,
889
+ FIVE_MONTHS: 150,
890
+ SIX_MONTHS: 180,
891
+ ONE_YEAR: 365,
892
+ THIRTEEN_MONTHS: 400,
893
+ EIGHTEEN_MONTHS: 545,
894
+ TWO_YEARS: 731,
895
+ FIVE_YEARS: 1827,
896
+ TEN_YEARS: 3653,
897
+ NEVER_EXPIRE: undefined,
898
+ } as const
899
+
900
+ /**
901
+ * Common metric filter patterns
902
+ */
903
+ static readonly FilterPatterns = {
904
+ /**
905
+ * Match ERROR log lines
906
+ */
907
+ errors: '[time, request_id, event_type = ERROR*, ...]',
908
+
909
+ /**
910
+ * Match all log lines
911
+ */
912
+ all: '',
913
+
914
+ /**
915
+ * Match JSON logs with specific field
916
+ */
917
+ jsonField: (field: string, value: string): string => `{ $.${field} = "${value}" }`,
918
+
919
+ /**
920
+ * Match HTTP status codes
921
+ */
922
+ httpStatus: (statusCode: number): string => `[..., status_code = ${statusCode}, ...]`,
923
+
924
+ /**
925
+ * Match 4xx errors
926
+ */
927
+ http4xx: '[..., status_code = 4*, ...]',
928
+
929
+ /**
930
+ * Match 5xx errors
931
+ */
932
+ http5xx: '[..., status_code = 5*, ...]',
933
+ } as const
934
+
935
+ /**
936
+ * Create a comprehensive application dashboard
937
+ */
938
+ static createApplicationDashboard(options: {
939
+ slug: string
940
+ environment: EnvironmentType
941
+ region?: string
942
+ components?: {
943
+ ec2InstanceIds?: string[]
944
+ lambdaFunctionNames?: string[]
945
+ ecsClusterName?: string
946
+ ecsServiceName?: string
947
+ albName?: string
948
+ targetGroupName?: string
949
+ rdsInstanceId?: string
950
+ sqsQueueNames?: string[]
951
+ logGroupNames?: string[]
952
+ }
953
+ }): {
954
+ dashboard: CloudWatchDashboard
955
+ logicalId: string
956
+ } {
957
+ const {
958
+ slug,
959
+ environment,
960
+ region = 'us-east-1',
961
+ components = {},
962
+ } = options
963
+
964
+ const widgets: DashboardWidget[] = []
965
+ let currentY = 0
966
+
967
+ // Header text widget
968
+ widgets.push({
969
+ type: 'text',
970
+ x: 0,
971
+ y: currentY,
972
+ width: 24,
973
+ height: 1,
974
+ properties: {
975
+ markdown: `# ${slug.toUpperCase()} Dashboard (${environment})`,
976
+ },
977
+ })
978
+ currentY += 1
979
+
980
+ // EC2 metrics
981
+ if (components.ec2InstanceIds && components.ec2InstanceIds.length > 0) {
982
+ widgets.push({
983
+ type: 'text',
984
+ x: 0,
985
+ y: currentY,
986
+ width: 24,
987
+ height: 1,
988
+ properties: {
989
+ markdown: '## EC2 Instances',
990
+ },
991
+ })
992
+ currentY += 1
993
+
994
+ // CPU utilization
995
+ widgets.push({
996
+ type: 'metric',
997
+ x: 0,
998
+ y: currentY,
999
+ width: 8,
1000
+ height: 6,
1001
+ properties: {
1002
+ title: 'CPU Utilization',
1003
+ region,
1004
+ metrics: components.ec2InstanceIds.map(id => [
1005
+ 'AWS/EC2',
1006
+ 'CPUUtilization',
1007
+ 'InstanceId',
1008
+ id,
1009
+ ]),
1010
+ period: 300,
1011
+ stat: 'Average',
1012
+ },
1013
+ })
1014
+
1015
+ // Network In/Out
1016
+ widgets.push({
1017
+ type: 'metric',
1018
+ x: 8,
1019
+ y: currentY,
1020
+ width: 8,
1021
+ height: 6,
1022
+ properties: {
1023
+ title: 'Network Traffic',
1024
+ region,
1025
+ metrics: [
1026
+ ...components.ec2InstanceIds.flatMap(id => [
1027
+ ['AWS/EC2', 'NetworkIn', 'InstanceId', id, { label: `${id} In` }],
1028
+ ['AWS/EC2', 'NetworkOut', 'InstanceId', id, { label: `${id} Out` }],
1029
+ ]),
1030
+ ],
1031
+ period: 300,
1032
+ stat: 'Sum',
1033
+ },
1034
+ })
1035
+
1036
+ // Status check
1037
+ widgets.push({
1038
+ type: 'metric',
1039
+ x: 16,
1040
+ y: currentY,
1041
+ width: 8,
1042
+ height: 6,
1043
+ properties: {
1044
+ title: 'Status Checks',
1045
+ region,
1046
+ metrics: components.ec2InstanceIds.map(id => [
1047
+ 'AWS/EC2',
1048
+ 'StatusCheckFailed',
1049
+ 'InstanceId',
1050
+ id,
1051
+ ]),
1052
+ period: 60,
1053
+ stat: 'Maximum',
1054
+ },
1055
+ })
1056
+
1057
+ currentY += 6
1058
+ }
1059
+
1060
+ // Lambda metrics
1061
+ if (components.lambdaFunctionNames && components.lambdaFunctionNames.length > 0) {
1062
+ widgets.push({
1063
+ type: 'text',
1064
+ x: 0,
1065
+ y: currentY,
1066
+ width: 24,
1067
+ height: 1,
1068
+ properties: {
1069
+ markdown: '## Lambda Functions',
1070
+ },
1071
+ })
1072
+ currentY += 1
1073
+
1074
+ // Invocations
1075
+ widgets.push({
1076
+ type: 'metric',
1077
+ x: 0,
1078
+ y: currentY,
1079
+ width: 8,
1080
+ height: 6,
1081
+ properties: {
1082
+ title: 'Invocations',
1083
+ region,
1084
+ metrics: components.lambdaFunctionNames.map(name => [
1085
+ 'AWS/Lambda',
1086
+ 'Invocations',
1087
+ 'FunctionName',
1088
+ name,
1089
+ ]),
1090
+ period: 300,
1091
+ stat: 'Sum',
1092
+ },
1093
+ })
1094
+
1095
+ // Duration
1096
+ widgets.push({
1097
+ type: 'metric',
1098
+ x: 8,
1099
+ y: currentY,
1100
+ width: 8,
1101
+ height: 6,
1102
+ properties: {
1103
+ title: 'Duration',
1104
+ region,
1105
+ metrics: components.lambdaFunctionNames.map(name => [
1106
+ 'AWS/Lambda',
1107
+ 'Duration',
1108
+ 'FunctionName',
1109
+ name,
1110
+ ]),
1111
+ period: 300,
1112
+ stat: 'Average',
1113
+ },
1114
+ })
1115
+
1116
+ // Errors
1117
+ widgets.push({
1118
+ type: 'metric',
1119
+ x: 16,
1120
+ y: currentY,
1121
+ width: 8,
1122
+ height: 6,
1123
+ properties: {
1124
+ title: 'Errors',
1125
+ region,
1126
+ metrics: components.lambdaFunctionNames.map(name => [
1127
+ 'AWS/Lambda',
1128
+ 'Errors',
1129
+ 'FunctionName',
1130
+ name,
1131
+ ]),
1132
+ period: 300,
1133
+ stat: 'Sum',
1134
+ },
1135
+ })
1136
+
1137
+ currentY += 6
1138
+ }
1139
+
1140
+ // ECS metrics
1141
+ if (components.ecsClusterName && components.ecsServiceName) {
1142
+ widgets.push({
1143
+ type: 'text',
1144
+ x: 0,
1145
+ y: currentY,
1146
+ width: 24,
1147
+ height: 1,
1148
+ properties: {
1149
+ markdown: '## ECS Service',
1150
+ },
1151
+ })
1152
+ currentY += 1
1153
+
1154
+ // CPU utilization
1155
+ widgets.push({
1156
+ type: 'metric',
1157
+ x: 0,
1158
+ y: currentY,
1159
+ width: 8,
1160
+ height: 6,
1161
+ properties: {
1162
+ title: 'CPU Utilization',
1163
+ region,
1164
+ metrics: [
1165
+ ['AWS/ECS', 'CPUUtilization', 'ClusterName', components.ecsClusterName, 'ServiceName', components.ecsServiceName],
1166
+ ],
1167
+ period: 300,
1168
+ stat: 'Average',
1169
+ },
1170
+ })
1171
+
1172
+ // Memory utilization
1173
+ widgets.push({
1174
+ type: 'metric',
1175
+ x: 8,
1176
+ y: currentY,
1177
+ width: 8,
1178
+ height: 6,
1179
+ properties: {
1180
+ title: 'Memory Utilization',
1181
+ region,
1182
+ metrics: [
1183
+ ['AWS/ECS', 'MemoryUtilization', 'ClusterName', components.ecsClusterName, 'ServiceName', components.ecsServiceName],
1184
+ ],
1185
+ period: 300,
1186
+ stat: 'Average',
1187
+ },
1188
+ })
1189
+
1190
+ // Running tasks
1191
+ widgets.push({
1192
+ type: 'metric',
1193
+ x: 16,
1194
+ y: currentY,
1195
+ width: 8,
1196
+ height: 6,
1197
+ properties: {
1198
+ title: 'Running Tasks',
1199
+ region,
1200
+ metrics: [
1201
+ ['ECS/ContainerInsights', 'RunningTaskCount', 'ClusterName', components.ecsClusterName, 'ServiceName', components.ecsServiceName],
1202
+ ],
1203
+ period: 60,
1204
+ stat: 'Average',
1205
+ },
1206
+ })
1207
+
1208
+ currentY += 6
1209
+ }
1210
+
1211
+ // ALB metrics
1212
+ if (components.albName) {
1213
+ widgets.push({
1214
+ type: 'text',
1215
+ x: 0,
1216
+ y: currentY,
1217
+ width: 24,
1218
+ height: 1,
1219
+ properties: {
1220
+ markdown: '## Application Load Balancer',
1221
+ },
1222
+ })
1223
+ currentY += 1
1224
+
1225
+ // Request count
1226
+ widgets.push({
1227
+ type: 'metric',
1228
+ x: 0,
1229
+ y: currentY,
1230
+ width: 8,
1231
+ height: 6,
1232
+ properties: {
1233
+ title: 'Request Count',
1234
+ region,
1235
+ metrics: [
1236
+ ['AWS/ApplicationELB', 'RequestCount', 'LoadBalancer', components.albName],
1237
+ ],
1238
+ period: 60,
1239
+ stat: 'Sum',
1240
+ },
1241
+ })
1242
+
1243
+ // Response time
1244
+ widgets.push({
1245
+ type: 'metric',
1246
+ x: 8,
1247
+ y: currentY,
1248
+ width: 8,
1249
+ height: 6,
1250
+ properties: {
1251
+ title: 'Response Time',
1252
+ region,
1253
+ metrics: [
1254
+ ['AWS/ApplicationELB', 'TargetResponseTime', 'LoadBalancer', components.albName],
1255
+ ],
1256
+ period: 60,
1257
+ stat: 'Average',
1258
+ },
1259
+ })
1260
+
1261
+ // HTTP errors
1262
+ widgets.push({
1263
+ type: 'metric',
1264
+ x: 16,
1265
+ y: currentY,
1266
+ width: 8,
1267
+ height: 6,
1268
+ properties: {
1269
+ title: 'HTTP Errors',
1270
+ region,
1271
+ metrics: [
1272
+ ['AWS/ApplicationELB', 'HTTPCode_Target_4XX_Count', 'LoadBalancer', components.albName, { label: '4XX' }],
1273
+ ['AWS/ApplicationELB', 'HTTPCode_Target_5XX_Count', 'LoadBalancer', components.albName, { label: '5XX' }],
1274
+ ],
1275
+ period: 60,
1276
+ stat: 'Sum',
1277
+ },
1278
+ })
1279
+
1280
+ currentY += 6
1281
+ }
1282
+
1283
+ // RDS metrics
1284
+ if (components.rdsInstanceId) {
1285
+ widgets.push({
1286
+ type: 'text',
1287
+ x: 0,
1288
+ y: currentY,
1289
+ width: 24,
1290
+ height: 1,
1291
+ properties: {
1292
+ markdown: '## RDS Database',
1293
+ },
1294
+ })
1295
+ currentY += 1
1296
+
1297
+ // CPU
1298
+ widgets.push({
1299
+ type: 'metric',
1300
+ x: 0,
1301
+ y: currentY,
1302
+ width: 8,
1303
+ height: 6,
1304
+ properties: {
1305
+ title: 'CPU Utilization',
1306
+ region,
1307
+ metrics: [
1308
+ ['AWS/RDS', 'CPUUtilization', 'DBInstanceIdentifier', components.rdsInstanceId],
1309
+ ],
1310
+ period: 300,
1311
+ stat: 'Average',
1312
+ },
1313
+ })
1314
+
1315
+ // Connections
1316
+ widgets.push({
1317
+ type: 'metric',
1318
+ x: 8,
1319
+ y: currentY,
1320
+ width: 8,
1321
+ height: 6,
1322
+ properties: {
1323
+ title: 'Database Connections',
1324
+ region,
1325
+ metrics: [
1326
+ ['AWS/RDS', 'DatabaseConnections', 'DBInstanceIdentifier', components.rdsInstanceId],
1327
+ ],
1328
+ period: 60,
1329
+ stat: 'Sum',
1330
+ },
1331
+ })
1332
+
1333
+ // Free storage
1334
+ widgets.push({
1335
+ type: 'metric',
1336
+ x: 16,
1337
+ y: currentY,
1338
+ width: 8,
1339
+ height: 6,
1340
+ properties: {
1341
+ title: 'Free Storage Space',
1342
+ region,
1343
+ metrics: [
1344
+ ['AWS/RDS', 'FreeStorageSpace', 'DBInstanceIdentifier', components.rdsInstanceId],
1345
+ ],
1346
+ period: 300,
1347
+ stat: 'Average',
1348
+ },
1349
+ })
1350
+
1351
+ currentY += 6
1352
+ }
1353
+
1354
+ // SQS metrics
1355
+ if (components.sqsQueueNames && components.sqsQueueNames.length > 0) {
1356
+ widgets.push({
1357
+ type: 'text',
1358
+ x: 0,
1359
+ y: currentY,
1360
+ width: 24,
1361
+ height: 1,
1362
+ properties: {
1363
+ markdown: '## SQS Queues',
1364
+ },
1365
+ })
1366
+ currentY += 1
1367
+
1368
+ // Messages visible
1369
+ widgets.push({
1370
+ type: 'metric',
1371
+ x: 0,
1372
+ y: currentY,
1373
+ width: 12,
1374
+ height: 6,
1375
+ properties: {
1376
+ title: 'Messages Visible',
1377
+ region,
1378
+ metrics: components.sqsQueueNames.map(name => [
1379
+ 'AWS/SQS',
1380
+ 'ApproximateNumberOfMessagesVisible',
1381
+ 'QueueName',
1382
+ name,
1383
+ ]),
1384
+ period: 60,
1385
+ stat: 'Sum',
1386
+ },
1387
+ })
1388
+
1389
+ // Age of oldest message
1390
+ widgets.push({
1391
+ type: 'metric',
1392
+ x: 12,
1393
+ y: currentY,
1394
+ width: 12,
1395
+ height: 6,
1396
+ properties: {
1397
+ title: 'Message Age',
1398
+ region,
1399
+ metrics: components.sqsQueueNames.map(name => [
1400
+ 'AWS/SQS',
1401
+ 'ApproximateAgeOfOldestMessage',
1402
+ 'QueueName',
1403
+ name,
1404
+ ]),
1405
+ period: 60,
1406
+ stat: 'Maximum',
1407
+ },
1408
+ })
1409
+
1410
+ currentY += 6
1411
+ }
1412
+
1413
+ // Log widget
1414
+ if (components.logGroupNames && components.logGroupNames.length > 0) {
1415
+ widgets.push({
1416
+ type: 'text',
1417
+ x: 0,
1418
+ y: currentY,
1419
+ width: 24,
1420
+ height: 1,
1421
+ properties: {
1422
+ markdown: '## Application Logs',
1423
+ },
1424
+ })
1425
+ currentY += 1
1426
+
1427
+ widgets.push({
1428
+ type: 'log',
1429
+ x: 0,
1430
+ y: currentY,
1431
+ width: 24,
1432
+ height: 6,
1433
+ properties: {
1434
+ query: `SOURCE '${components.logGroupNames.join("' | SOURCE '")}'
1435
+ | fields @timestamp, @message
1436
+ | filter @message like /ERROR|WARN|Exception/
1437
+ | sort @timestamp desc
1438
+ | limit 50`,
1439
+ region,
1440
+ title: 'Recent Errors and Warnings',
1441
+ },
1442
+ })
1443
+
1444
+ currentY += 6
1445
+ }
1446
+
1447
+ return Monitoring.createDashboard({
1448
+ slug,
1449
+ environment,
1450
+ widgets,
1451
+ })
1452
+ }
1453
+
1454
+ /**
1455
+ * Dashboard templates for common architectures
1456
+ */
1457
+ static readonly DashboardTemplates = {
1458
+ /**
1459
+ * Static website dashboard (S3 + CloudFront)
1460
+ */
1461
+ staticWebsite: (options: {
1462
+ slug: string
1463
+ environment: EnvironmentType
1464
+ region?: string
1465
+ cloudFrontDistributionId: string
1466
+ s3BucketName: string
1467
+ }): DashboardOptions => ({
1468
+ slug: options.slug,
1469
+ environment: options.environment,
1470
+ widgets: [
1471
+ {
1472
+ type: 'text',
1473
+ x: 0,
1474
+ y: 0,
1475
+ width: 24,
1476
+ height: 1,
1477
+ properties: {
1478
+ markdown: `# ${options.slug.toUpperCase()} Static Website Dashboard`,
1479
+ },
1480
+ },
1481
+ {
1482
+ type: 'metric',
1483
+ x: 0,
1484
+ y: 1,
1485
+ width: 8,
1486
+ height: 6,
1487
+ properties: {
1488
+ title: 'CloudFront Requests',
1489
+ region: 'us-east-1',
1490
+ metrics: [
1491
+ ['AWS/CloudFront', 'Requests', 'DistributionId', options.cloudFrontDistributionId, 'Region', 'Global'],
1492
+ ],
1493
+ period: 300,
1494
+ stat: 'Sum',
1495
+ },
1496
+ },
1497
+ {
1498
+ type: 'metric',
1499
+ x: 8,
1500
+ y: 1,
1501
+ width: 8,
1502
+ height: 6,
1503
+ properties: {
1504
+ title: 'Error Rate',
1505
+ region: 'us-east-1',
1506
+ metrics: [
1507
+ ['AWS/CloudFront', '4xxErrorRate', 'DistributionId', options.cloudFrontDistributionId, 'Region', 'Global', { label: '4XX' }],
1508
+ ['AWS/CloudFront', '5xxErrorRate', 'DistributionId', options.cloudFrontDistributionId, 'Region', 'Global', { label: '5XX' }],
1509
+ ],
1510
+ period: 300,
1511
+ stat: 'Average',
1512
+ },
1513
+ },
1514
+ {
1515
+ type: 'metric',
1516
+ x: 16,
1517
+ y: 1,
1518
+ width: 8,
1519
+ height: 6,
1520
+ properties: {
1521
+ title: 'Bytes Downloaded',
1522
+ region: 'us-east-1',
1523
+ metrics: [
1524
+ ['AWS/CloudFront', 'BytesDownloaded', 'DistributionId', options.cloudFrontDistributionId, 'Region', 'Global'],
1525
+ ],
1526
+ period: 300,
1527
+ stat: 'Sum',
1528
+ },
1529
+ },
1530
+ {
1531
+ type: 'metric',
1532
+ x: 0,
1533
+ y: 7,
1534
+ width: 12,
1535
+ height: 6,
1536
+ properties: {
1537
+ title: 'S3 Bucket Size',
1538
+ region: options.region || 'us-east-1',
1539
+ metrics: [
1540
+ ['AWS/S3', 'BucketSizeBytes', 'BucketName', options.s3BucketName, 'StorageType', 'StandardStorage'],
1541
+ ],
1542
+ period: 86400,
1543
+ stat: 'Average',
1544
+ },
1545
+ },
1546
+ {
1547
+ type: 'metric',
1548
+ x: 12,
1549
+ y: 7,
1550
+ width: 12,
1551
+ height: 6,
1552
+ properties: {
1553
+ title: 'S3 Number of Objects',
1554
+ region: options.region || 'us-east-1',
1555
+ metrics: [
1556
+ ['AWS/S3', 'NumberOfObjects', 'BucketName', options.s3BucketName, 'StorageType', 'AllStorageTypes'],
1557
+ ],
1558
+ period: 86400,
1559
+ stat: 'Average',
1560
+ },
1561
+ },
1562
+ ],
1563
+ }),
1564
+
1565
+ /**
1566
+ * Serverless API dashboard (Lambda + API Gateway)
1567
+ */
1568
+ serverlessApi: (options: {
1569
+ slug: string
1570
+ environment: EnvironmentType
1571
+ region?: string
1572
+ apiGatewayName: string
1573
+ lambdaFunctionNames: string[]
1574
+ }): DashboardOptions => ({
1575
+ slug: options.slug,
1576
+ environment: options.environment,
1577
+ widgets: [
1578
+ {
1579
+ type: 'text',
1580
+ x: 0,
1581
+ y: 0,
1582
+ width: 24,
1583
+ height: 1,
1584
+ properties: {
1585
+ markdown: `# ${options.slug.toUpperCase()} Serverless API Dashboard`,
1586
+ },
1587
+ },
1588
+ {
1589
+ type: 'metric',
1590
+ x: 0,
1591
+ y: 1,
1592
+ width: 8,
1593
+ height: 6,
1594
+ properties: {
1595
+ title: 'API Requests',
1596
+ region: options.region || 'us-east-1',
1597
+ metrics: [
1598
+ ['AWS/ApiGateway', 'Count', 'ApiName', options.apiGatewayName],
1599
+ ],
1600
+ period: 60,
1601
+ stat: 'Sum',
1602
+ },
1603
+ },
1604
+ {
1605
+ type: 'metric',
1606
+ x: 8,
1607
+ y: 1,
1608
+ width: 8,
1609
+ height: 6,
1610
+ properties: {
1611
+ title: 'API Latency',
1612
+ region: options.region || 'us-east-1',
1613
+ metrics: [
1614
+ ['AWS/ApiGateway', 'Latency', 'ApiName', options.apiGatewayName],
1615
+ ],
1616
+ period: 60,
1617
+ stat: 'Average',
1618
+ },
1619
+ },
1620
+ {
1621
+ type: 'metric',
1622
+ x: 16,
1623
+ y: 1,
1624
+ width: 8,
1625
+ height: 6,
1626
+ properties: {
1627
+ title: 'API Errors',
1628
+ region: options.region || 'us-east-1',
1629
+ metrics: [
1630
+ ['AWS/ApiGateway', '4XXError', 'ApiName', options.apiGatewayName, { label: '4XX' }],
1631
+ ['AWS/ApiGateway', '5XXError', 'ApiName', options.apiGatewayName, { label: '5XX' }],
1632
+ ],
1633
+ period: 60,
1634
+ stat: 'Sum',
1635
+ },
1636
+ },
1637
+ ...options.lambdaFunctionNames.map((name, index) => ({
1638
+ type: 'metric' as const,
1639
+ x: (index % 3) * 8,
1640
+ y: 7 + Math.floor(index / 3) * 6,
1641
+ width: 8,
1642
+ height: 6,
1643
+ properties: {
1644
+ title: `${name} Metrics`,
1645
+ region: options.region || 'us-east-1',
1646
+ metrics: [
1647
+ ['AWS/Lambda', 'Invocations', 'FunctionName', name],
1648
+ ['AWS/Lambda', 'Duration', 'FunctionName', name],
1649
+ ['AWS/Lambda', 'Errors', 'FunctionName', name],
1650
+ ],
1651
+ period: 300,
1652
+ stat: 'Sum',
1653
+ },
1654
+ })),
1655
+ ],
1656
+ }),
1657
+
1658
+ /**
1659
+ * Container service dashboard (ECS + ALB)
1660
+ */
1661
+ containerService: (options: {
1662
+ slug: string
1663
+ environment: EnvironmentType
1664
+ region?: string
1665
+ ecsClusterName: string
1666
+ ecsServiceName: string
1667
+ albName: string
1668
+ rdsInstanceId?: string
1669
+ }): DashboardOptions => ({
1670
+ slug: options.slug,
1671
+ environment: options.environment,
1672
+ widgets: [
1673
+ {
1674
+ type: 'text',
1675
+ x: 0,
1676
+ y: 0,
1677
+ width: 24,
1678
+ height: 1,
1679
+ properties: {
1680
+ markdown: `# ${options.slug.toUpperCase()} Container Service Dashboard`,
1681
+ },
1682
+ },
1683
+ // ECS metrics
1684
+ {
1685
+ type: 'metric',
1686
+ x: 0,
1687
+ y: 1,
1688
+ width: 8,
1689
+ height: 6,
1690
+ properties: {
1691
+ title: 'ECS CPU',
1692
+ region: options.region || 'us-east-1',
1693
+ metrics: [
1694
+ ['AWS/ECS', 'CPUUtilization', 'ClusterName', options.ecsClusterName, 'ServiceName', options.ecsServiceName],
1695
+ ],
1696
+ period: 60,
1697
+ stat: 'Average',
1698
+ },
1699
+ },
1700
+ {
1701
+ type: 'metric',
1702
+ x: 8,
1703
+ y: 1,
1704
+ width: 8,
1705
+ height: 6,
1706
+ properties: {
1707
+ title: 'ECS Memory',
1708
+ region: options.region || 'us-east-1',
1709
+ metrics: [
1710
+ ['AWS/ECS', 'MemoryUtilization', 'ClusterName', options.ecsClusterName, 'ServiceName', options.ecsServiceName],
1711
+ ],
1712
+ period: 60,
1713
+ stat: 'Average',
1714
+ },
1715
+ },
1716
+ {
1717
+ type: 'metric',
1718
+ x: 16,
1719
+ y: 1,
1720
+ width: 8,
1721
+ height: 6,
1722
+ properties: {
1723
+ title: 'Running Tasks',
1724
+ region: options.region || 'us-east-1',
1725
+ metrics: [
1726
+ ['ECS/ContainerInsights', 'RunningTaskCount', 'ClusterName', options.ecsClusterName, 'ServiceName', options.ecsServiceName],
1727
+ ],
1728
+ period: 60,
1729
+ stat: 'Average',
1730
+ },
1731
+ },
1732
+ // ALB metrics
1733
+ {
1734
+ type: 'metric',
1735
+ x: 0,
1736
+ y: 7,
1737
+ width: 8,
1738
+ height: 6,
1739
+ properties: {
1740
+ title: 'ALB Requests',
1741
+ region: options.region || 'us-east-1',
1742
+ metrics: [
1743
+ ['AWS/ApplicationELB', 'RequestCount', 'LoadBalancer', options.albName],
1744
+ ],
1745
+ period: 60,
1746
+ stat: 'Sum',
1747
+ },
1748
+ },
1749
+ {
1750
+ type: 'metric',
1751
+ x: 8,
1752
+ y: 7,
1753
+ width: 8,
1754
+ height: 6,
1755
+ properties: {
1756
+ title: 'Response Time',
1757
+ region: options.region || 'us-east-1',
1758
+ metrics: [
1759
+ ['AWS/ApplicationELB', 'TargetResponseTime', 'LoadBalancer', options.albName],
1760
+ ],
1761
+ period: 60,
1762
+ stat: 'Average',
1763
+ },
1764
+ },
1765
+ {
1766
+ type: 'metric',
1767
+ x: 16,
1768
+ y: 7,
1769
+ width: 8,
1770
+ height: 6,
1771
+ properties: {
1772
+ title: 'Healthy Hosts',
1773
+ region: options.region || 'us-east-1',
1774
+ metrics: [
1775
+ ['AWS/ApplicationELB', 'HealthyHostCount', 'LoadBalancer', options.albName],
1776
+ ],
1777
+ period: 60,
1778
+ stat: 'Average',
1779
+ },
1780
+ },
1781
+ ...(options.rdsInstanceId
1782
+ ? [
1783
+ {
1784
+ type: 'metric' as const,
1785
+ x: 0,
1786
+ y: 13,
1787
+ width: 12,
1788
+ height: 6,
1789
+ properties: {
1790
+ title: 'RDS CPU',
1791
+ region: options.region || 'us-east-1',
1792
+ metrics: [
1793
+ ['AWS/RDS', 'CPUUtilization', 'DBInstanceIdentifier', options.rdsInstanceId],
1794
+ ],
1795
+ period: 300,
1796
+ stat: 'Average',
1797
+ },
1798
+ },
1799
+ {
1800
+ type: 'metric' as const,
1801
+ x: 12,
1802
+ y: 13,
1803
+ width: 12,
1804
+ height: 6,
1805
+ properties: {
1806
+ title: 'RDS Connections',
1807
+ region: options.region || 'us-east-1',
1808
+ metrics: [
1809
+ ['AWS/RDS', 'DatabaseConnections', 'DBInstanceIdentifier', options.rdsInstanceId],
1810
+ ],
1811
+ period: 60,
1812
+ stat: 'Sum',
1813
+ },
1814
+ },
1815
+ ]
1816
+ : []),
1817
+ ],
1818
+ }),
1819
+ }
1820
+
1821
+ /**
1822
+ * Monitoring Configuration helpers
1823
+ * Provides Stacks configuration parity for monitoring options
1824
+ */
1825
+ static readonly Config = {
1826
+ /**
1827
+ * Create alarm configuration
1828
+ */
1829
+ createAlarmConfig: (options: {
1830
+ metricName: string
1831
+ namespace: string
1832
+ threshold: number
1833
+ comparisonOperator?: 'GreaterThanThreshold' | 'GreaterThanOrEqualToThreshold' | 'LessThanThreshold' | 'LessThanOrEqualToThreshold'
1834
+ evaluationPeriods?: number
1835
+ period?: number
1836
+ statistic?: 'Average' | 'Sum' | 'Maximum' | 'Minimum' | 'SampleCount'
1837
+ treatMissingData?: 'breaching' | 'notBreaching' | 'ignore' | 'missing'
1838
+ }): {
1839
+ MetricName: string
1840
+ Namespace: string
1841
+ Threshold: number
1842
+ ComparisonOperator: string
1843
+ EvaluationPeriods: number
1844
+ Period: number
1845
+ Statistic: string
1846
+ TreatMissingData: string
1847
+ } => {
1848
+ const {
1849
+ metricName,
1850
+ namespace,
1851
+ threshold,
1852
+ comparisonOperator = 'GreaterThanThreshold',
1853
+ evaluationPeriods = 1,
1854
+ period = 300,
1855
+ statistic = 'Average',
1856
+ treatMissingData = 'missing',
1857
+ } = options
1858
+
1859
+ return {
1860
+ MetricName: metricName,
1861
+ Namespace: namespace,
1862
+ Threshold: threshold,
1863
+ ComparisonOperator: comparisonOperator,
1864
+ EvaluationPeriods: evaluationPeriods,
1865
+ Period: period,
1866
+ Statistic: statistic,
1867
+ TreatMissingData: treatMissingData,
1868
+ }
1869
+ },
1870
+
1871
+ /**
1872
+ * AWS namespace constants
1873
+ */
1874
+ namespaces: {
1875
+ ec2: 'AWS/EC2',
1876
+ ecs: 'AWS/ECS',
1877
+ lambda: 'AWS/Lambda',
1878
+ rds: 'AWS/RDS',
1879
+ sqs: 'AWS/SQS',
1880
+ sns: 'AWS/SNS',
1881
+ s3: 'AWS/S3',
1882
+ cloudfront: 'AWS/CloudFront',
1883
+ alb: 'AWS/ApplicationELB',
1884
+ nlb: 'AWS/NetworkELB',
1885
+ apiGateway: 'AWS/ApiGateway',
1886
+ dynamodb: 'AWS/DynamoDB',
1887
+ elasticache: 'AWS/ElastiCache',
1888
+ } as const,
1889
+
1890
+ /**
1891
+ * Comparison operator options
1892
+ */
1893
+ comparisonOperators: {
1894
+ greaterThan: 'GreaterThanThreshold',
1895
+ greaterOrEqual: 'GreaterThanOrEqualToThreshold',
1896
+ lessThan: 'LessThanThreshold',
1897
+ lessOrEqual: 'LessThanOrEqualToThreshold',
1898
+ } as const,
1899
+
1900
+ /**
1901
+ * Common metric configurations by service
1902
+ */
1903
+ metrics: {
1904
+ ec2: {
1905
+ cpu: { metricName: 'CPUUtilization', namespace: 'AWS/EC2' },
1906
+ networkIn: { metricName: 'NetworkIn', namespace: 'AWS/EC2' },
1907
+ networkOut: { metricName: 'NetworkOut', namespace: 'AWS/EC2' },
1908
+ statusCheck: { metricName: 'StatusCheckFailed', namespace: 'AWS/EC2' },
1909
+ },
1910
+ ecs: {
1911
+ cpu: { metricName: 'CPUUtilization', namespace: 'AWS/ECS' },
1912
+ memory: { metricName: 'MemoryUtilization', namespace: 'AWS/ECS' },
1913
+ },
1914
+ lambda: {
1915
+ invocations: { metricName: 'Invocations', namespace: 'AWS/Lambda' },
1916
+ errors: { metricName: 'Errors', namespace: 'AWS/Lambda' },
1917
+ duration: { metricName: 'Duration', namespace: 'AWS/Lambda' },
1918
+ throttles: { metricName: 'Throttles', namespace: 'AWS/Lambda' },
1919
+ concurrentExecutions: { metricName: 'ConcurrentExecutions', namespace: 'AWS/Lambda' },
1920
+ },
1921
+ rds: {
1922
+ cpu: { metricName: 'CPUUtilization', namespace: 'AWS/RDS' },
1923
+ connections: { metricName: 'DatabaseConnections', namespace: 'AWS/RDS' },
1924
+ freeStorage: { metricName: 'FreeStorageSpace', namespace: 'AWS/RDS' },
1925
+ readLatency: { metricName: 'ReadLatency', namespace: 'AWS/RDS' },
1926
+ writeLatency: { metricName: 'WriteLatency', namespace: 'AWS/RDS' },
1927
+ },
1928
+ alb: {
1929
+ requestCount: { metricName: 'RequestCount', namespace: 'AWS/ApplicationELB' },
1930
+ responseTime: { metricName: 'TargetResponseTime', namespace: 'AWS/ApplicationELB' },
1931
+ httpCode4xx: { metricName: 'HTTPCode_Target_4XX_Count', namespace: 'AWS/ApplicationELB' },
1932
+ httpCode5xx: { metricName: 'HTTPCode_Target_5XX_Count', namespace: 'AWS/ApplicationELB' },
1933
+ healthyHosts: { metricName: 'HealthyHostCount', namespace: 'AWS/ApplicationELB' },
1934
+ },
1935
+ sqs: {
1936
+ messagesVisible: { metricName: 'ApproximateNumberOfMessagesVisible', namespace: 'AWS/SQS' },
1937
+ messagesDelayed: { metricName: 'ApproximateNumberOfMessagesDelayed', namespace: 'AWS/SQS' },
1938
+ messageAge: { metricName: 'ApproximateAgeOfOldestMessage', namespace: 'AWS/SQS' },
1939
+ },
1940
+ },
1941
+
1942
+ /**
1943
+ * Common alarm presets
1944
+ */
1945
+ presets: {
1946
+ /**
1947
+ * High CPU alarm
1948
+ */
1949
+ highCpu: (threshold: number = 80): {
1950
+ metricName: string;
1951
+ threshold: number;
1952
+ comparisonOperator: 'GreaterThanThreshold';
1953
+ evaluationPeriods: number;
1954
+ period: number;
1955
+ statistic: 'Average';
1956
+ } => ({
1957
+ metricName: 'CPUUtilization',
1958
+ threshold,
1959
+ comparisonOperator: 'GreaterThanThreshold' as const,
1960
+ evaluationPeriods: 3,
1961
+ period: 300,
1962
+ statistic: 'Average' as const,
1963
+ }),
1964
+
1965
+ /**
1966
+ * High memory alarm (for ECS)
1967
+ */
1968
+ highMemory: (threshold: number = 80): {
1969
+ metricName: string;
1970
+ threshold: number;
1971
+ comparisonOperator: 'GreaterThanThreshold';
1972
+ evaluationPeriods: number;
1973
+ period: number;
1974
+ statistic: 'Average';
1975
+ } => ({
1976
+ metricName: 'MemoryUtilization',
1977
+ threshold,
1978
+ comparisonOperator: 'GreaterThanThreshold' as const,
1979
+ evaluationPeriods: 3,
1980
+ period: 300,
1981
+ statistic: 'Average' as const,
1982
+ }),
1983
+
1984
+ /**
1985
+ * High error rate alarm
1986
+ */
1987
+ highErrors: (threshold: number = 10): {
1988
+ metricName: string;
1989
+ threshold: number;
1990
+ comparisonOperator: 'GreaterThanThreshold';
1991
+ evaluationPeriods: number;
1992
+ period: number;
1993
+ statistic: 'Sum';
1994
+ } => ({
1995
+ metricName: 'Errors',
1996
+ threshold,
1997
+ comparisonOperator: 'GreaterThanThreshold' as const,
1998
+ evaluationPeriods: 1,
1999
+ period: 60,
2000
+ statistic: 'Sum' as const,
2001
+ }),
2002
+
2003
+ /**
2004
+ * High latency alarm
2005
+ */
2006
+ highLatency: (threshold: number = 5000): {
2007
+ metricName: string;
2008
+ threshold: number;
2009
+ comparisonOperator: 'GreaterThanThreshold';
2010
+ evaluationPeriods: number;
2011
+ period: number;
2012
+ statistic: 'Average';
2013
+ } => ({
2014
+ metricName: 'Duration',
2015
+ threshold,
2016
+ comparisonOperator: 'GreaterThanThreshold' as const,
2017
+ evaluationPeriods: 3,
2018
+ period: 300,
2019
+ statistic: 'Average' as const,
2020
+ }),
2021
+
2022
+ /**
2023
+ * Low healthy hosts alarm
2024
+ */
2025
+ lowHealthyHosts: (threshold: number = 1): {
2026
+ metricName: string;
2027
+ namespace: string;
2028
+ threshold: number;
2029
+ comparisonOperator: 'LessThanThreshold';
2030
+ evaluationPeriods: number;
2031
+ period: number;
2032
+ statistic: 'Minimum';
2033
+ } => ({
2034
+ metricName: 'HealthyHostCount',
2035
+ namespace: 'AWS/ApplicationELB',
2036
+ threshold,
2037
+ comparisonOperator: 'LessThanThreshold' as const,
2038
+ evaluationPeriods: 2,
2039
+ period: 60,
2040
+ statistic: 'Minimum' as const,
2041
+ }),
2042
+
2043
+ /**
2044
+ * Queue depth alarm
2045
+ */
2046
+ queueDepth: (threshold: number = 1000): {
2047
+ metricName: string;
2048
+ namespace: string;
2049
+ threshold: number;
2050
+ comparisonOperator: 'GreaterThanThreshold';
2051
+ evaluationPeriods: number;
2052
+ period: number;
2053
+ statistic: 'Average';
2054
+ } => ({
2055
+ metricName: 'ApproximateNumberOfMessagesVisible',
2056
+ namespace: 'AWS/SQS',
2057
+ threshold,
2058
+ comparisonOperator: 'GreaterThanThreshold' as const,
2059
+ evaluationPeriods: 3,
2060
+ period: 300,
2061
+ statistic: 'Average' as const,
2062
+ }),
2063
+
2064
+ /**
2065
+ * Low storage alarm
2066
+ */
2067
+ lowStorage: (threshold: number = 10737418240): {
2068
+ metricName: string;
2069
+ namespace: string;
2070
+ threshold: number;
2071
+ comparisonOperator: 'LessThanThreshold';
2072
+ evaluationPeriods: number;
2073
+ period: number;
2074
+ statistic: 'Average';
2075
+ } => ({ // 10 GB
2076
+ metricName: 'FreeStorageSpace',
2077
+ namespace: 'AWS/RDS',
2078
+ threshold,
2079
+ comparisonOperator: 'LessThanThreshold' as const,
2080
+ evaluationPeriods: 1,
2081
+ period: 300,
2082
+ statistic: 'Average' as const,
2083
+ }),
2084
+ },
2085
+ }
2086
+ }