@friggframework/devtools 2.0.0-next.61 → 2.0.0-next.62

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 (305) hide show
  1. package/package.json +14 -7
  2. package/.eslintrc.json +0 -3
  3. package/CHANGELOG.md +0 -132
  4. package/infrastructure/ARCHITECTURE.md +0 -487
  5. package/infrastructure/CLAUDE.md +0 -481
  6. package/infrastructure/HEALTH.md +0 -468
  7. package/infrastructure/README.md +0 -522
  8. package/infrastructure/__tests__/fixtures/mock-aws-resources.js +0 -391
  9. package/infrastructure/__tests__/helpers/test-utils.js +0 -277
  10. package/infrastructure/__tests__/postgres-config.test.js +0 -914
  11. package/infrastructure/__tests__/template-generation.test.js +0 -687
  12. package/infrastructure/create-frigg-infrastructure.js +0 -147
  13. package/infrastructure/docs/POSTGRES-CONFIGURATION.md +0 -630
  14. package/infrastructure/docs/PRE-DEPLOYMENT-HEALTH-CHECK-SPEC.md +0 -1317
  15. package/infrastructure/docs/WEBSOCKET-CONFIGURATION.md +0 -105
  16. package/infrastructure/docs/deployment-instructions.md +0 -268
  17. package/infrastructure/docs/generate-iam-command.md +0 -278
  18. package/infrastructure/docs/iam-policy-templates.md +0 -193
  19. package/infrastructure/domains/database/aurora-builder.js +0 -809
  20. package/infrastructure/domains/database/aurora-builder.test.js +0 -950
  21. package/infrastructure/domains/database/aurora-discovery.js +0 -87
  22. package/infrastructure/domains/database/aurora-discovery.test.js +0 -188
  23. package/infrastructure/domains/database/aurora-resolver.js +0 -210
  24. package/infrastructure/domains/database/aurora-resolver.test.js +0 -347
  25. package/infrastructure/domains/database/migration-builder.js +0 -701
  26. package/infrastructure/domains/database/migration-builder.test.js +0 -321
  27. package/infrastructure/domains/database/migration-resolver.js +0 -163
  28. package/infrastructure/domains/database/migration-resolver.test.js +0 -337
  29. package/infrastructure/domains/health/application/ports/IPropertyReconciler.js +0 -164
  30. package/infrastructure/domains/health/application/ports/IResourceDetector.js +0 -129
  31. package/infrastructure/domains/health/application/ports/IResourceImporter.js +0 -142
  32. package/infrastructure/domains/health/application/ports/IStackRepository.js +0 -131
  33. package/infrastructure/domains/health/application/ports/index.js +0 -26
  34. package/infrastructure/domains/health/application/use-cases/__tests__/execute-resource-import-use-case.test.js +0 -679
  35. package/infrastructure/domains/health/application/use-cases/__tests__/mismatch-analyzer-method-name.test.js +0 -167
  36. package/infrastructure/domains/health/application/use-cases/__tests__/repair-via-import-use-case.test.js +0 -1130
  37. package/infrastructure/domains/health/application/use-cases/execute-resource-import-use-case.js +0 -221
  38. package/infrastructure/domains/health/application/use-cases/reconcile-properties-use-case.js +0 -152
  39. package/infrastructure/domains/health/application/use-cases/reconcile-properties-use-case.test.js +0 -343
  40. package/infrastructure/domains/health/application/use-cases/repair-via-import-use-case.js +0 -535
  41. package/infrastructure/domains/health/application/use-cases/repair-via-import-use-case.test.js +0 -376
  42. package/infrastructure/domains/health/application/use-cases/run-health-check-use-case.js +0 -213
  43. package/infrastructure/domains/health/application/use-cases/run-health-check-use-case.test.js +0 -441
  44. package/infrastructure/domains/health/docs/ACME-DEV-DRIFT-ANALYSIS.md +0 -267
  45. package/infrastructure/domains/health/docs/BUILD-VS-DEPLOYED-TEMPLATE-ANALYSIS.md +0 -324
  46. package/infrastructure/domains/health/docs/ORPHAN-DETECTION-ANALYSIS.md +0 -386
  47. package/infrastructure/domains/health/docs/SPEC-CLEANUP-COMMAND.md +0 -1419
  48. package/infrastructure/domains/health/docs/TDD-IMPLEMENTATION-SUMMARY.md +0 -391
  49. package/infrastructure/domains/health/docs/TEMPLATE-COMPARISON-IMPLEMENTATION.md +0 -551
  50. package/infrastructure/domains/health/domain/entities/issue.js +0 -299
  51. package/infrastructure/domains/health/domain/entities/issue.test.js +0 -528
  52. package/infrastructure/domains/health/domain/entities/property-mismatch.js +0 -108
  53. package/infrastructure/domains/health/domain/entities/property-mismatch.test.js +0 -275
  54. package/infrastructure/domains/health/domain/entities/resource.js +0 -159
  55. package/infrastructure/domains/health/domain/entities/resource.test.js +0 -432
  56. package/infrastructure/domains/health/domain/entities/stack-health-report.js +0 -306
  57. package/infrastructure/domains/health/domain/entities/stack-health-report.test.js +0 -601
  58. package/infrastructure/domains/health/domain/services/__tests__/health-score-percentage-based.test.js +0 -380
  59. package/infrastructure/domains/health/domain/services/__tests__/import-progress-monitor.test.js +0 -971
  60. package/infrastructure/domains/health/domain/services/__tests__/import-template-generator.test.js +0 -1150
  61. package/infrastructure/domains/health/domain/services/__tests__/logical-id-mapper.test.js +0 -672
  62. package/infrastructure/domains/health/domain/services/__tests__/template-parser.test.js +0 -496
  63. package/infrastructure/domains/health/domain/services/__tests__/update-progress-monitor.test.js +0 -419
  64. package/infrastructure/domains/health/domain/services/health-score-calculator.js +0 -248
  65. package/infrastructure/domains/health/domain/services/health-score-calculator.test.js +0 -504
  66. package/infrastructure/domains/health/domain/services/import-progress-monitor.js +0 -195
  67. package/infrastructure/domains/health/domain/services/import-template-generator.js +0 -435
  68. package/infrastructure/domains/health/domain/services/logical-id-mapper.js +0 -345
  69. package/infrastructure/domains/health/domain/services/mismatch-analyzer.js +0 -234
  70. package/infrastructure/domains/health/domain/services/mismatch-analyzer.test.js +0 -431
  71. package/infrastructure/domains/health/domain/services/property-mutability-config.js +0 -382
  72. package/infrastructure/domains/health/domain/services/template-parser.js +0 -245
  73. package/infrastructure/domains/health/domain/services/update-progress-monitor.js +0 -192
  74. package/infrastructure/domains/health/domain/value-objects/health-score.js +0 -138
  75. package/infrastructure/domains/health/domain/value-objects/health-score.test.js +0 -267
  76. package/infrastructure/domains/health/domain/value-objects/property-mutability.js +0 -161
  77. package/infrastructure/domains/health/domain/value-objects/property-mutability.test.js +0 -198
  78. package/infrastructure/domains/health/domain/value-objects/resource-state.js +0 -167
  79. package/infrastructure/domains/health/domain/value-objects/resource-state.test.js +0 -196
  80. package/infrastructure/domains/health/domain/value-objects/stack-identifier.js +0 -192
  81. package/infrastructure/domains/health/domain/value-objects/stack-identifier.test.js +0 -262
  82. package/infrastructure/domains/health/infrastructure/adapters/__tests__/orphan-detection-cfn-tagged.test.js +0 -312
  83. package/infrastructure/domains/health/infrastructure/adapters/__tests__/orphan-detection-multi-stack.test.js +0 -367
  84. package/infrastructure/domains/health/infrastructure/adapters/__tests__/orphan-detection-relationship-analysis.test.js +0 -432
  85. package/infrastructure/domains/health/infrastructure/adapters/aws-property-reconciler.js +0 -784
  86. package/infrastructure/domains/health/infrastructure/adapters/aws-property-reconciler.test.js +0 -1133
  87. package/infrastructure/domains/health/infrastructure/adapters/aws-resource-detector.js +0 -565
  88. package/infrastructure/domains/health/infrastructure/adapters/aws-resource-detector.test.js +0 -554
  89. package/infrastructure/domains/health/infrastructure/adapters/aws-resource-importer.js +0 -318
  90. package/infrastructure/domains/health/infrastructure/adapters/aws-resource-importer.test.js +0 -398
  91. package/infrastructure/domains/health/infrastructure/adapters/aws-stack-repository.js +0 -777
  92. package/infrastructure/domains/health/infrastructure/adapters/aws-stack-repository.test.js +0 -580
  93. package/infrastructure/domains/integration/integration-builder.js +0 -404
  94. package/infrastructure/domains/integration/integration-builder.test.js +0 -690
  95. package/infrastructure/domains/integration/integration-resolver.js +0 -170
  96. package/infrastructure/domains/integration/integration-resolver.test.js +0 -369
  97. package/infrastructure/domains/integration/websocket-builder.js +0 -69
  98. package/infrastructure/domains/integration/websocket-builder.test.js +0 -195
  99. package/infrastructure/domains/networking/vpc-builder.js +0 -2051
  100. package/infrastructure/domains/networking/vpc-builder.test.js +0 -1960
  101. package/infrastructure/domains/networking/vpc-discovery.js +0 -177
  102. package/infrastructure/domains/networking/vpc-discovery.test.js +0 -350
  103. package/infrastructure/domains/networking/vpc-resolver.js +0 -505
  104. package/infrastructure/domains/networking/vpc-resolver.test.js +0 -801
  105. package/infrastructure/domains/parameters/ssm-builder.js +0 -79
  106. package/infrastructure/domains/parameters/ssm-builder.test.js +0 -189
  107. package/infrastructure/domains/parameters/ssm-discovery.js +0 -84
  108. package/infrastructure/domains/parameters/ssm-discovery.test.js +0 -210
  109. package/infrastructure/domains/security/iam-generator.js +0 -816
  110. package/infrastructure/domains/security/iam-generator.test.js +0 -204
  111. package/infrastructure/domains/security/kms-builder.js +0 -415
  112. package/infrastructure/domains/security/kms-builder.test.js +0 -392
  113. package/infrastructure/domains/security/kms-discovery.js +0 -80
  114. package/infrastructure/domains/security/kms-discovery.test.js +0 -177
  115. package/infrastructure/domains/security/kms-resolver.js +0 -96
  116. package/infrastructure/domains/security/kms-resolver.test.js +0 -216
  117. package/infrastructure/domains/security/templates/frigg-deployment-iam-stack.yaml +0 -401
  118. package/infrastructure/domains/security/templates/iam-policy-basic.json +0 -218
  119. package/infrastructure/domains/security/templates/iam-policy-full.json +0 -288
  120. package/infrastructure/domains/shared/base-builder.js +0 -112
  121. package/infrastructure/domains/shared/base-resolver.js +0 -186
  122. package/infrastructure/domains/shared/base-resolver.test.js +0 -305
  123. package/infrastructure/domains/shared/builder-orchestrator.js +0 -212
  124. package/infrastructure/domains/shared/builder-orchestrator.test.js +0 -213
  125. package/infrastructure/domains/shared/cloudformation-discovery-v2.js +0 -334
  126. package/infrastructure/domains/shared/cloudformation-discovery.js +0 -672
  127. package/infrastructure/domains/shared/cloudformation-discovery.test.js +0 -985
  128. package/infrastructure/domains/shared/environment-builder.js +0 -119
  129. package/infrastructure/domains/shared/environment-builder.test.js +0 -247
  130. package/infrastructure/domains/shared/providers/aws-provider-adapter.js +0 -579
  131. package/infrastructure/domains/shared/providers/aws-provider-adapter.test.js +0 -416
  132. package/infrastructure/domains/shared/providers/azure-provider-adapter.stub.js +0 -93
  133. package/infrastructure/domains/shared/providers/cloud-provider-adapter.js +0 -136
  134. package/infrastructure/domains/shared/providers/gcp-provider-adapter.stub.js +0 -82
  135. package/infrastructure/domains/shared/providers/provider-factory.js +0 -108
  136. package/infrastructure/domains/shared/providers/provider-factory.test.js +0 -170
  137. package/infrastructure/domains/shared/resource-discovery.enhanced.test.js +0 -306
  138. package/infrastructure/domains/shared/resource-discovery.js +0 -233
  139. package/infrastructure/domains/shared/resource-discovery.test.js +0 -588
  140. package/infrastructure/domains/shared/types/app-definition.js +0 -205
  141. package/infrastructure/domains/shared/types/discovery-result.js +0 -106
  142. package/infrastructure/domains/shared/types/discovery-result.test.js +0 -258
  143. package/infrastructure/domains/shared/types/index.js +0 -46
  144. package/infrastructure/domains/shared/types/resource-ownership.js +0 -108
  145. package/infrastructure/domains/shared/types/resource-ownership.test.js +0 -101
  146. package/infrastructure/domains/shared/utilities/base-definition-factory.js +0 -394
  147. package/infrastructure/domains/shared/utilities/base-definition-factory.js.bak +0 -338
  148. package/infrastructure/domains/shared/utilities/base-definition-factory.test.js +0 -291
  149. package/infrastructure/domains/shared/utilities/handler-path-resolver.js +0 -134
  150. package/infrastructure/domains/shared/utilities/handler-path-resolver.test.js +0 -268
  151. package/infrastructure/domains/shared/utilities/prisma-layer-manager.js +0 -159
  152. package/infrastructure/domains/shared/utilities/prisma-layer-manager.test.js +0 -444
  153. package/infrastructure/domains/shared/validation/env-validator.js +0 -78
  154. package/infrastructure/domains/shared/validation/env-validator.test.js +0 -173
  155. package/infrastructure/domains/shared/validation/plugin-validator.js +0 -187
  156. package/infrastructure/domains/shared/validation/plugin-validator.test.js +0 -323
  157. package/infrastructure/esbuild.config.js +0 -53
  158. package/infrastructure/index.js +0 -4
  159. package/infrastructure/infrastructure-composer.js +0 -117
  160. package/infrastructure/infrastructure-composer.test.js +0 -1895
  161. package/infrastructure/integration.test.js +0 -383
  162. package/infrastructure/scripts/build-prisma-layer.js +0 -701
  163. package/infrastructure/scripts/build-prisma-layer.test.js +0 -170
  164. package/infrastructure/scripts/build-time-discovery.js +0 -238
  165. package/infrastructure/scripts/build-time-discovery.test.js +0 -379
  166. package/infrastructure/scripts/run-discovery.js +0 -110
  167. package/infrastructure/scripts/verify-prisma-layer.js +0 -72
  168. package/layers/prisma/.build-complete +0 -3
  169. package/layers/prisma/nodejs/package.json +0 -8
  170. package/management-ui/.eslintrc.js +0 -22
  171. package/management-ui/components.json +0 -21
  172. package/management-ui/docs/phase2-integration-guide.md +0 -320
  173. package/management-ui/index.html +0 -13
  174. package/management-ui/package.json +0 -76
  175. package/management-ui/packages/devtools/frigg-cli/ui-command/index.js +0 -302
  176. package/management-ui/postcss.config.js +0 -6
  177. package/management-ui/server/api/backend.js +0 -256
  178. package/management-ui/server/api/cli.js +0 -315
  179. package/management-ui/server/api/codegen.js +0 -663
  180. package/management-ui/server/api/connections.js +0 -857
  181. package/management-ui/server/api/discovery.js +0 -185
  182. package/management-ui/server/api/environment/index.js +0 -1
  183. package/management-ui/server/api/environment/router.js +0 -378
  184. package/management-ui/server/api/environment.js +0 -328
  185. package/management-ui/server/api/integrations.js +0 -876
  186. package/management-ui/server/api/logs.js +0 -248
  187. package/management-ui/server/api/monitoring.js +0 -282
  188. package/management-ui/server/api/open-ide.js +0 -31
  189. package/management-ui/server/api/project.js +0 -1029
  190. package/management-ui/server/api/users/sessions.js +0 -371
  191. package/management-ui/server/api/users/simulation.js +0 -254
  192. package/management-ui/server/api/users.js +0 -362
  193. package/management-ui/server/api-contract.md +0 -275
  194. package/management-ui/server/index.js +0 -873
  195. package/management-ui/server/middleware/errorHandler.js +0 -93
  196. package/management-ui/server/middleware/security.js +0 -32
  197. package/management-ui/server/processManager.js +0 -296
  198. package/management-ui/server/server.js +0 -346
  199. package/management-ui/server/services/aws-monitor.js +0 -413
  200. package/management-ui/server/services/npm-registry.js +0 -347
  201. package/management-ui/server/services/template-engine.js +0 -538
  202. package/management-ui/server/utils/cliIntegration.js +0 -220
  203. package/management-ui/server/utils/environment/auditLogger.js +0 -471
  204. package/management-ui/server/utils/environment/awsParameterStore.js +0 -275
  205. package/management-ui/server/utils/environment/encryption.js +0 -278
  206. package/management-ui/server/utils/environment/envFileManager.js +0 -286
  207. package/management-ui/server/utils/import-commonjs.js +0 -28
  208. package/management-ui/server/utils/response.js +0 -83
  209. package/management-ui/server/websocket/handler.js +0 -325
  210. package/management-ui/src/App.jsx +0 -25
  211. package/management-ui/src/assets/FriggLogo.svg +0 -1
  212. package/management-ui/src/components/AppRouter.jsx +0 -65
  213. package/management-ui/src/components/Button.jsx +0 -70
  214. package/management-ui/src/components/Card.jsx +0 -97
  215. package/management-ui/src/components/EnvironmentCompare.jsx +0 -400
  216. package/management-ui/src/components/EnvironmentEditor.jsx +0 -372
  217. package/management-ui/src/components/EnvironmentImportExport.jsx +0 -469
  218. package/management-ui/src/components/EnvironmentSchema.jsx +0 -491
  219. package/management-ui/src/components/EnvironmentSecurity.jsx +0 -463
  220. package/management-ui/src/components/ErrorBoundary.jsx +0 -73
  221. package/management-ui/src/components/IntegrationCard.jsx +0 -481
  222. package/management-ui/src/components/IntegrationCardEnhanced.jsx +0 -770
  223. package/management-ui/src/components/IntegrationExplorer.jsx +0 -379
  224. package/management-ui/src/components/IntegrationStatus.jsx +0 -336
  225. package/management-ui/src/components/Layout.jsx +0 -716
  226. package/management-ui/src/components/LoadingSpinner.jsx +0 -113
  227. package/management-ui/src/components/RepositoryPicker.jsx +0 -248
  228. package/management-ui/src/components/SessionMonitor.jsx +0 -350
  229. package/management-ui/src/components/StatusBadge.jsx +0 -208
  230. package/management-ui/src/components/UserContextSwitcher.jsx +0 -212
  231. package/management-ui/src/components/UserSimulation.jsx +0 -327
  232. package/management-ui/src/components/Welcome.jsx +0 -434
  233. package/management-ui/src/components/codegen/APIEndpointGenerator.jsx +0 -637
  234. package/management-ui/src/components/codegen/APIModuleSelector.jsx +0 -227
  235. package/management-ui/src/components/codegen/CodeGenerationWizard.jsx +0 -247
  236. package/management-ui/src/components/codegen/CodePreviewEditor.jsx +0 -316
  237. package/management-ui/src/components/codegen/DynamicModuleForm.jsx +0 -271
  238. package/management-ui/src/components/codegen/FormBuilder.jsx +0 -737
  239. package/management-ui/src/components/codegen/IntegrationGenerator.jsx +0 -855
  240. package/management-ui/src/components/codegen/ProjectScaffoldWizard.jsx +0 -797
  241. package/management-ui/src/components/codegen/SchemaBuilder.jsx +0 -303
  242. package/management-ui/src/components/codegen/TemplateSelector.jsx +0 -586
  243. package/management-ui/src/components/codegen/index.js +0 -10
  244. package/management-ui/src/components/connections/ConnectionConfigForm.jsx +0 -362
  245. package/management-ui/src/components/connections/ConnectionHealthMonitor.jsx +0 -182
  246. package/management-ui/src/components/connections/ConnectionTester.jsx +0 -200
  247. package/management-ui/src/components/connections/EntityRelationshipMapper.jsx +0 -292
  248. package/management-ui/src/components/connections/OAuthFlow.jsx +0 -204
  249. package/management-ui/src/components/connections/index.js +0 -5
  250. package/management-ui/src/components/index.js +0 -21
  251. package/management-ui/src/components/monitoring/APIGatewayMetrics.jsx +0 -222
  252. package/management-ui/src/components/monitoring/LambdaMetrics.jsx +0 -169
  253. package/management-ui/src/components/monitoring/MetricsChart.jsx +0 -197
  254. package/management-ui/src/components/monitoring/MonitoringDashboard.jsx +0 -393
  255. package/management-ui/src/components/monitoring/SQSMetrics.jsx +0 -246
  256. package/management-ui/src/components/monitoring/index.js +0 -6
  257. package/management-ui/src/components/monitoring/monitoring.css +0 -218
  258. package/management-ui/src/components/theme-provider.jsx +0 -52
  259. package/management-ui/src/components/theme-toggle.jsx +0 -39
  260. package/management-ui/src/components/ui/badge.tsx +0 -36
  261. package/management-ui/src/components/ui/button.test.jsx +0 -56
  262. package/management-ui/src/components/ui/button.tsx +0 -57
  263. package/management-ui/src/components/ui/card.tsx +0 -76
  264. package/management-ui/src/components/ui/dropdown-menu.tsx +0 -199
  265. package/management-ui/src/components/ui/select.tsx +0 -157
  266. package/management-ui/src/components/ui/skeleton.jsx +0 -15
  267. package/management-ui/src/hooks/useFrigg.jsx +0 -387
  268. package/management-ui/src/hooks/useSocket.jsx +0 -58
  269. package/management-ui/src/index.css +0 -193
  270. package/management-ui/src/lib/utils.ts +0 -6
  271. package/management-ui/src/main.jsx +0 -10
  272. package/management-ui/src/pages/CodeGeneration.jsx +0 -14
  273. package/management-ui/src/pages/Connections.jsx +0 -252
  274. package/management-ui/src/pages/ConnectionsEnhanced.jsx +0 -633
  275. package/management-ui/src/pages/Dashboard.jsx +0 -311
  276. package/management-ui/src/pages/Environment.jsx +0 -314
  277. package/management-ui/src/pages/IntegrationConfigure.jsx +0 -669
  278. package/management-ui/src/pages/IntegrationDiscovery.jsx +0 -567
  279. package/management-ui/src/pages/IntegrationTest.jsx +0 -742
  280. package/management-ui/src/pages/Integrations.jsx +0 -253
  281. package/management-ui/src/pages/Monitoring.jsx +0 -17
  282. package/management-ui/src/pages/Simulation.jsx +0 -155
  283. package/management-ui/src/pages/Users.jsx +0 -492
  284. package/management-ui/src/services/api.js +0 -41
  285. package/management-ui/src/services/apiModuleService.js +0 -193
  286. package/management-ui/src/services/websocket-handlers.js +0 -120
  287. package/management-ui/src/test/api/project.test.js +0 -273
  288. package/management-ui/src/test/components/Welcome.test.jsx +0 -378
  289. package/management-ui/src/test/mocks/server.js +0 -178
  290. package/management-ui/src/test/setup.js +0 -61
  291. package/management-ui/src/test/utils/test-utils.jsx +0 -134
  292. package/management-ui/src/utils/repository.js +0 -98
  293. package/management-ui/src/utils/repository.test.js +0 -118
  294. package/management-ui/src/workflows/phase2-integration-workflows.js +0 -884
  295. package/management-ui/tailwind.config.js +0 -63
  296. package/management-ui/tsconfig.json +0 -37
  297. package/management-ui/tsconfig.node.json +0 -10
  298. package/management-ui/vite.config.js +0 -26
  299. package/management-ui/vitest.config.js +0 -38
  300. package/test/auther-definition-method-tester.js +0 -45
  301. package/test/index.js +0 -9
  302. package/test/integration-validator.js +0 -2
  303. package/test/mock-api-readme.md +0 -102
  304. package/test/mock-api.js +0 -284
  305. package/test/mock-integration.js +0 -78
@@ -1,1419 +0,0 @@
1
- # Specification: Cleanup Command for Orphaned Resources
2
-
3
- **Version**: 1.0.0
4
- **Status**: Draft
5
- **Created**: 2025-10-27
6
- **Author**: Claude Code (following user requirements)
7
-
8
- ## Overview
9
-
10
- Create a new `frigg cleanup --orphaned` command to safely delete duplicate orphaned resources that are not part of the current CloudFormation stack template. This command helps users clean up leftover resources from previous deployments or failed stacks.
11
-
12
- ## Business Context
13
-
14
- ### Problem Statement
15
-
16
- After implementing logical ID mapping and deduplication for `frigg repair --import`, we discovered that many stacks have duplicate orphaned resources:
17
-
18
- - Resources with CloudFormation tags (indicating they were once managed)
19
- - NOT referenced in the current build template (indicating they're no longer needed)
20
- - Example: 3 VPCs all tagged `FriggVPC`, but only 1 is in the current template
21
-
22
- These duplicate resources:
23
- - Cost money (especially VPCs, NAT Gateways, Elastic IPs)
24
- - Clutter AWS accounts
25
- - Cause confusion during troubleshooting
26
- - Are potential security risks (orphaned security groups with open rules)
27
-
28
- ### User Story
29
-
30
- > As a DevOps engineer managing Frigg applications
31
- > I want to safely delete duplicate orphaned resources
32
- > So that I can reduce costs, improve account hygiene, and eliminate security risks
33
- > While avoiding accidental deletion of resources that are still in use
34
-
35
- ### Real-World Example
36
-
37
- From `quo-integrations-dev` stack:
38
- - **16 orphaned resources detected**
39
- - **5 will be imported** (in build template)
40
- - **11 are duplicates** that should be cleaned up:
41
- - 2 extra VPCs ($36/month each for NAT Gateway)
42
- - 7 extra Subnets ($0 but clutter)
43
- - 2 extra SecurityGroups (potential security risk)
44
-
45
- **Estimated monthly savings**: ~$72 for just the NAT Gateways
46
-
47
- ## Requirements
48
-
49
- ### Functional Requirements
50
-
51
- #### FR-1: Command Interface
52
-
53
- ```bash
54
- # Dry-run mode (default) - show what would be deleted
55
- frigg cleanup --orphaned
56
- frigg cleanup --orphaned --dry-run
57
-
58
- # Execute deletion
59
- frigg cleanup --orphaned --execute
60
-
61
- # Target specific stack
62
- frigg cleanup --orphaned --stack quo-integrations-dev --execute
63
-
64
- # Auto-confirm (skip confirmation prompts)
65
- frigg cleanup --orphaned --execute --yes
66
-
67
- # Clean up specific resource types only
68
- frigg cleanup --orphaned --resource-type AWS::EC2::VPC --execute
69
- frigg cleanup --orphaned --resource-type AWS::EC2::Subnet --execute
70
-
71
- # Filter by logical ID pattern
72
- frigg cleanup --orphaned --logical-id "Frigg*" --execute
73
-
74
- # JSON output for scripting
75
- frigg cleanup --orphaned --output json
76
- ```
77
-
78
- #### FR-2: Safety Features
79
-
80
- **CRITICAL SAFETY REQUIREMENTS**:
81
-
82
- 1. **Dry-run by default**: Unless `--execute` is specified, only show what would be deleted
83
- 2. **Dependency checking**: Detect and warn about dependencies before deletion
84
- 3. **Confirmation prompts**: Require explicit user confirmation before deletion
85
- 4. **Deletion order**: Delete resources in correct order (subnets before VPCs, etc.)
86
- 5. **Rollback protection**: Create snapshots/backups where possible
87
- 6. **Audit logging**: Log all deletion attempts and results
88
-
89
- **Dependency Detection Examples**:
90
-
91
- ```javascript
92
- // VPC dependency check
93
- if (resourceType === 'AWS::EC2::VPC') {
94
- const dependencies = [
95
- await checkForSubnets(vpcId),
96
- await checkForSecurityGroups(vpcId),
97
- await checkForNATGateways(vpcId),
98
- await checkForInternetGateways(vpcId),
99
- await checkForVPCEndpoints(vpcId),
100
- await checkForRouteTables(vpcId),
101
- await checkForNetworkACLs(vpcId),
102
- ];
103
-
104
- if (dependencies.some(d => d.hasResources)) {
105
- throw new Error('Cannot delete VPC: has dependent resources');
106
- }
107
- }
108
-
109
- // Security Group dependency check
110
- if (resourceType === 'AWS::EC2::SecurityGroup') {
111
- const usage = [
112
- await checkForEC2Instances(sgId),
113
- await checkForRDSInstances(sgId),
114
- await checkForLambdaFunctions(sgId),
115
- await checkForLoadBalancers(sgId),
116
- ];
117
-
118
- if (usage.some(u => u.hasResources)) {
119
- throw new Error('Cannot delete SecurityGroup: in use by other resources');
120
- }
121
- }
122
- ```
123
-
124
- #### FR-3: Deletion Order
125
-
126
- Resources must be deleted in dependency order:
127
-
128
- **Phase 1 - Detach Dependencies**:
129
- 1. VPC Endpoints
130
- 2. NAT Gateway attachments
131
- 3. Internet Gateway attachments
132
- 4. Route table associations
133
-
134
- **Phase 2 - Delete Dependent Resources**:
135
- 1. NAT Gateways
136
- 2. Internet Gateways
137
- 3. Route Tables (non-default)
138
- 4. Network ACLs (non-default)
139
- 5. Subnets
140
- 6. Security Groups (non-default)
141
-
142
- **Phase 3 - Delete Core Resources**:
143
- 1. VPCs
144
-
145
- **Implementation**:
146
-
147
- ```javascript
148
- const DELETION_ORDER = [
149
- { type: 'AWS::EC2::VPCEndpoint', phase: 1 },
150
- { type: 'AWS::EC2::NatGateway', phase: 2 },
151
- { type: 'AWS::EC2::InternetGateway', phase: 2 },
152
- { type: 'AWS::EC2::RouteTable', phase: 2 },
153
- { type: 'AWS::EC2::NetworkAcl', phase: 2 },
154
- { type: 'AWS::EC2::Subnet', phase: 2 },
155
- { type: 'AWS::EC2::SecurityGroup', phase: 2 },
156
- { type: 'AWS::EC2::VPC', phase: 3 },
157
- ];
158
- ```
159
-
160
- #### FR-4: Terminal Output
161
-
162
- **Dry-Run Mode** (default):
163
-
164
- ```bash
165
- $ frigg cleanup --orphaned
166
-
167
- 🧹 Frigg Cleanup - Orphaned Resources
168
- ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
169
-
170
- Stack: quo-integrations-dev
171
- Region: us-east-1
172
- Mode: DRY-RUN (no resources will be deleted)
173
-
174
- ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
175
-
176
- 📋 Analyzing duplicate orphaned resources...
177
-
178
- Found 11 duplicate resources not in build template:
179
-
180
- VPCs (2):
181
- ⚠️ vpc-0e2351eac99adcb83 (FriggVPC)
182
- • Tags: quo-integrations-dev, Stage: dev
183
- • Cost: ~$36/month (NAT Gateway)
184
- • Dependencies: 3 subnets, 1 security group
185
- • Status: Can be deleted (after dependencies)
186
-
187
- ⚠️ vpc-020a0365610c05f0b (FriggVPC)
188
- • Tags: quo-integrations-dev, Stage: dev
189
- • Cost: ~$36/month (NAT Gateway)
190
- • Dependencies: 2 subnets, 1 security group
191
- • Status: Can be deleted (after dependencies)
192
-
193
- Subnets (7):
194
- ✓ subnet-0123456789abcdef0 (FriggPrivateSubnet1)
195
- • Parent VPC: vpc-0e2351eac99adcb83
196
- • Status: Can be deleted
197
-
198
- ✓ subnet-0123456789abcdef1 (FriggPrivateSubnet2)
199
- • Parent VPC: vpc-0e2351eac99adcb83
200
- • Status: Can be deleted
201
-
202
- ... (5 more subnets)
203
-
204
- SecurityGroups (2):
205
- ⚠️ sg-0123456789abcdef0 (FriggLambdaSecurityGroup)
206
- • VPC: vpc-0e2351eac99adcb83
207
- • Rules: 2 ingress, 1 egress
208
- • In use by: 0 resources
209
- • Status: Can be deleted
210
-
211
- ⚠️ sg-0123456789abcdef1 (FriggLambdaSecurityGroup)
212
- • VPC: vpc-020a0365610c05f0b
213
- • Rules: 2 ingress, 1 egress
214
- • In use by: 0 resources
215
- • Status: Can be deleted
216
-
217
- ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
218
-
219
- 📊 CLEANUP SUMMARY
220
-
221
- Total resources: 11
222
- • VPCs: 2 (can delete)
223
- • Subnets: 7 (can delete)
224
- • SecurityGroups: 2 (can delete)
225
-
226
- Estimated monthly savings: $72
227
-
228
- Deletion order:
229
- Phase 1: Remove attachments (0 resources)
230
- Phase 2: Delete dependent resources (9 resources)
231
- Phase 3: Delete core resources (2 VPCs)
232
-
233
- ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
234
-
235
- ⚠️ SAFETY WARNINGS
236
-
237
- • This operation cannot be easily undone
238
- • Resources will be permanently deleted from AWS
239
- • Verify no applications depend on these resources
240
- • Consider taking backups if needed
241
-
242
- ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
243
-
244
- To delete these resources, run:
245
- frigg cleanup --orphaned --execute
246
-
247
- To review individual resources:
248
- frigg cleanup --orphaned --output json | jq .
249
- ```
250
-
251
- **Execute Mode** (with confirmation):
252
-
253
- ```bash
254
- $ frigg cleanup --orphaned --execute
255
-
256
- 🧹 Frigg Cleanup - Orphaned Resources
257
- ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
258
-
259
- Stack: quo-integrations-dev
260
- Region: us-east-1
261
- Mode: EXECUTE (resources WILL be deleted)
262
-
263
- ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
264
-
265
- ⚠️ WARNING: You are about to DELETE 11 AWS resources
266
-
267
- This action:
268
- • Cannot be easily undone
269
- • Will permanently delete resources from AWS
270
- • May affect running applications if dependencies exist
271
- • Will save approximately $72/month
272
-
273
- Resources to delete:
274
- • 2 VPCs
275
- • 7 Subnets
276
- • 2 SecurityGroups
277
-
278
- ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
279
-
280
- Type 'delete quo-integrations-dev' to confirm: delete quo-integrations-dev
281
-
282
- ✓ Confirmation received. Starting deletion...
283
-
284
- ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
285
-
286
- Phase 1: Detaching dependencies...
287
- (no resources in this phase)
288
-
289
- Phase 2: Deleting dependent resources...
290
- [1/9] Deleting subnet-0123456789abcdef0... ✓
291
- [2/9] Deleting subnet-0123456789abcdef1... ✓
292
- [3/9] Deleting subnet-0123456789abcdef2... ✓
293
- [4/9] Deleting subnet-0123456789abcdef3... ✓
294
- [5/9] Deleting subnet-0123456789abcdef4... ✓
295
- [6/9] Deleting subnet-0123456789abcdef5... ✓
296
- [7/9] Deleting subnet-0123456789abcdef6... ✓
297
- [8/9] Deleting sg-0123456789abcdef0... ✓
298
- [9/9] Deleting sg-0123456789abcdef1... ✓
299
-
300
- Phase 3: Deleting core resources...
301
- [1/2] Deleting vpc-0e2351eac99adcb83... ✓ (20s)
302
- [2/2] Deleting vpc-020a0365610c05f0b... ✓ (18s)
303
-
304
- ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
305
-
306
- ✅ CLEANUP COMPLETE
307
-
308
- Successfully deleted: 11 resources
309
- • VPCs: 2
310
- • Subnets: 7
311
- • SecurityGroups: 2
312
-
313
- Failed: 0 resources
314
-
315
- Estimated monthly savings: $72
316
-
317
- Total time: 45s
318
-
319
- ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
320
-
321
- 💡 Next steps:
322
- 1. Run 'frigg doctor' to verify health score improved
323
- 2. Run 'frigg repair --import' to import remaining resources
324
- ```
325
-
326
- **Execute Mode** (with --yes flag):
327
-
328
- ```bash
329
- $ frigg cleanup --orphaned --execute --yes
330
-
331
- 🧹 Frigg Cleanup - Orphaned Resources
332
- ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
333
-
334
- ⚠️ Auto-confirm enabled (--yes flag)
335
-
336
- Deleting 11 resources without confirmation...
337
-
338
- Phase 2: Deleting dependent resources...
339
- [1/9] Deleting subnet-0123456789abcdef0... ✓
340
- ... (progress shown)
341
-
342
- ✅ CLEANUP COMPLETE
343
- ```
344
-
345
- #### FR-5: Error Handling
346
-
347
- **Dependency Errors**:
348
-
349
- ```bash
350
- $ frigg cleanup --orphaned --execute
351
-
352
- Phase 3: Deleting core resources...
353
- [1/2] Deleting vpc-0e2351eac99adcb83... ✗ FAILED
354
-
355
- ❌ Error: Cannot delete VPC vpc-0e2351eac99adcb83
356
- Reason: DependencyViolation - The vpc has dependencies and cannot be deleted
357
-
358
- Remaining dependencies:
359
- • Internet Gateway: igw-0123456789abcdef0 (still attached)
360
- • NAT Gateway: nat-0123456789abcdef0 (still exists)
361
-
362
- 💡 Run cleanup again to retry after dependency cleanup
363
- ```
364
-
365
- **Permission Errors**:
366
-
367
- ```bash
368
- ❌ Error: Access Denied
369
- Reason: User lacks permission to delete VPCs
370
-
371
- Required IAM permissions:
372
- • ec2:DeleteVpc
373
- • ec2:DeleteSubnet
374
- • ec2:DeleteSecurityGroup
375
-
376
- 💡 Update your IAM policy and try again
377
- ```
378
-
379
- **Resource In Use**:
380
-
381
- ```bash
382
- ⚠️ Cannot delete sg-0123456789abcdef0
383
-
384
- Reason: Security group is in use by:
385
- • Lambda function: quo-integrations-dev-handler (12345678)
386
- • RDS instance: quo-integrations-db (i-12345678)
387
-
388
- ❌ This resource will NOT be deleted
389
-
390
- 💡 Remove references from these resources first
391
- ```
392
-
393
- #### FR-6: JSON Output
394
-
395
- For scripting and automation:
396
-
397
- ```json
398
- {
399
- "stack": {
400
- "name": "quo-integrations-dev",
401
- "region": "us-east-1"
402
- },
403
- "mode": "dry-run",
404
- "resources": {
405
- "total": 11,
406
- "canDelete": 11,
407
- "blocked": 0,
408
- "byType": {
409
- "AWS::EC2::VPC": 2,
410
- "AWS::EC2::Subnet": 7,
411
- "AWS::EC2::SecurityGroup": 2
412
- }
413
- },
414
- "duplicateResources": [
415
- {
416
- "physicalId": "vpc-0e2351eac99adcb83",
417
- "resourceType": "AWS::EC2::VPC",
418
- "logicalId": "FriggVPC",
419
- "tags": {
420
- "aws:cloudformation:stack-name": "quo-integrations-dev",
421
- "aws:cloudformation:logical-id": "FriggVPC",
422
- "Stage": "dev"
423
- },
424
- "dependencies": {
425
- "subnets": ["subnet-0123456789abcdef0", "subnet-0123456789abcdef1", "subnet-0123456789abcdef2"],
426
- "securityGroups": ["sg-0123456789abcdef0"],
427
- "hasBlockingDependencies": false
428
- },
429
- "estimatedMonthlyCost": 36.00,
430
- "deletionPhase": 3,
431
- "canDelete": true,
432
- "blockingReason": null
433
- }
434
- // ... 10 more resources
435
- ],
436
- "deletionPlan": {
437
- "phase1": [],
438
- "phase2": [
439
- {
440
- "physicalId": "subnet-0123456789abcdef0",
441
- "resourceType": "AWS::EC2::Subnet",
442
- "order": 1
443
- }
444
- // ... 8 more
445
- ],
446
- "phase3": [
447
- {
448
- "physicalId": "vpc-0e2351eac99adcb83",
449
- "resourceType": "AWS::EC2::VPC",
450
- "order": 1
451
- },
452
- {
453
- "physicalId": "vpc-020a0365610c05f0b",
454
- "resourceType": "AWS::EC2::VPC",
455
- "order": 2
456
- }
457
- ]
458
- },
459
- "estimatedSavings": {
460
- "monthly": 72.00,
461
- "annual": 864.00
462
- },
463
- "warnings": [
464
- "This operation cannot be easily undone",
465
- "Resources will be permanently deleted from AWS",
466
- "Verify no applications depend on these resources"
467
- ],
468
- "timestamp": "2025-10-27T10:30:00Z"
469
- }
470
- ```
471
-
472
- ### Non-Functional Requirements
473
-
474
- #### NFR-1: Performance
475
-
476
- - Dependency checking must complete within 30 seconds for 50 resources
477
- - Deletion progress updates every 2 seconds
478
- - Support parallel deletion where safe (independent resources)
479
-
480
- #### NFR-2: Reliability
481
-
482
- - Idempotent operations (safe to retry after failure)
483
- - Continue on non-fatal errors
484
- - Comprehensive error logging
485
-
486
- #### NFR-3: Security
487
-
488
- - Require explicit confirmation for destructive operations
489
- - Log all deletion attempts to CloudWatch/audit log
490
- - Support AWS CloudTrail integration
491
- - Never expose credentials in logs
492
-
493
- #### NFR-4: Usability
494
-
495
- - Clear, informative error messages
496
- - Progress indicators for long operations
497
- - Cost estimates to help decision-making
498
- - Helpful next-step suggestions
499
-
500
- ## Design
501
-
502
- ### Architecture
503
-
504
- Following **Hexagonal Architecture** principles:
505
-
506
- ```
507
- ┌─────────────────────────────────────────────────────────────────┐
508
- │ Adapter Layer (CLI Command) │
509
- │ cleanup-command/index.js │
510
- │ - Parse command line options │
511
- │ - Handle confirmation prompts │
512
- │ - Format output (terminal, JSON) │
513
- │ - Display progress indicators │
514
- └────────────────┬────────────────────────────────────────────────┘
515
- │ calls
516
- ┌────────────────▼────────────────────────────────────────────────┐
517
- │ Application Layer (Use Case) │
518
- │ CleanupOrphanedResourcesUseCase (NEW) │
519
- │ - Orchestrate cleanup workflow │
520
- │ - Identify duplicate resources │
521
- │ - Check dependencies │
522
- │ - Plan deletion order │
523
- │ - Execute deletions with rollback │
524
- └────────────────┬────────────────────────────────────────────────┘
525
- │ calls
526
- ┌────────────────▼────────────────────────────────────────────────┐
527
- │ Domain Layer (Services) │
528
- │ OrphanedResourceCategorizerService (REUSE) │
529
- │ - Identify duplicate orphaned resources │
530
- │ │
531
- │ ResourceDependencyAnalyzer (NEW) │
532
- │ - Analyze resource dependencies │
533
- │ - Determine deletion order │
534
- │ - Check for blocking dependencies │
535
- │ │
536
- │ ResourceDeletionPlanner (NEW) │
537
- │ - Create deletion plan with phases │
538
- │ - Calculate cost savings │
539
- │ - Generate warnings │
540
- └────────────────┬────────────────────────────────────────────────┘
541
- │ uses
542
- ┌────────────────▼────────────────────────────────────────────────┐
543
- │ Infrastructure Layer (Repositories) │
544
- │ ResourceDeleterRepository (NEW) │
545
- │ - Execute AWS API calls for resource deletion │
546
- │ - Check resource dependencies via AWS APIs │
547
- │ - Handle AWS-specific errors │
548
- │ │
549
- │ AuditLogRepository (NEW) │
550
- │ - Log deletion attempts and results │
551
- │ - Store audit trail │
552
- └─────────────────────────────────────────────────────────────────┘
553
- ```
554
-
555
- ### Component Details
556
-
557
- #### 1. ResourceDependencyAnalyzer (NEW)
558
-
559
- **Location**: `packages/devtools/infrastructure/domains/health/domain/services/resource-dependency-analyzer.js`
560
-
561
- **Responsibilities**:
562
- - Analyze dependencies between resources
563
- - Determine safe deletion order
564
- - Detect blocking dependencies
565
-
566
- **Public Methods**:
567
-
568
- ```javascript
569
- class ResourceDependencyAnalyzer {
570
- constructor({ ec2Client, elbClient, rdsClient, lambdaClient }) {
571
- this.ec2 = ec2Client;
572
- this.elb = elbClient;
573
- this.rds = rdsClient;
574
- this.lambda = lambdaClient;
575
- }
576
-
577
- /**
578
- * Analyze dependencies for a list of resources
579
- *
580
- * @param {Array} resources - Resources to analyze
581
- * @returns {Promise<Object>} Dependency analysis
582
- */
583
- async analyzeDependencies(resources) {
584
- const analysis = {
585
- canDeleteAll: true,
586
- blockedResources: [],
587
- dependencies: {},
588
- };
589
-
590
- for (const resource of resources) {
591
- const deps = await this._checkResourceDependencies(resource);
592
-
593
- analysis.dependencies[resource.physicalId] = deps;
594
-
595
- if (deps.hasBlockingDependencies) {
596
- analysis.canDeleteAll = false;
597
- analysis.blockedResources.push({
598
- resource,
599
- blockingDependencies: deps.blocking,
600
- });
601
- }
602
- }
603
-
604
- return analysis;
605
- }
606
-
607
- /**
608
- * Determine deletion order based on dependencies
609
- *
610
- * @param {Array} resources - Resources to order
611
- * @returns {Object} Deletion plan with phases
612
- */
613
- determineDeletionOrder(resources) {
614
- const phases = {
615
- phase1: [], // Detach dependencies
616
- phase2: [], // Delete dependent resources
617
- phase3: [], // Delete core resources
618
- };
619
-
620
- for (const resource of resources) {
621
- const phase = this._getDeletionPhase(resource.resourceType);
622
- phases[`phase${phase}`].push(resource);
623
- }
624
-
625
- // Sort each phase by priority
626
- phases.phase1.sort(this._compareDeletionPriority);
627
- phases.phase2.sort(this._compareDeletionPriority);
628
- phases.phase3.sort(this._compareDeletionPriority);
629
-
630
- return phases;
631
- }
632
-
633
- /**
634
- * Check dependencies for a specific resource
635
- * @private
636
- */
637
- async _checkResourceDependencies(resource) {
638
- switch (resource.resourceType) {
639
- case 'AWS::EC2::VPC':
640
- return await this._checkVpcDependencies(resource.physicalId);
641
- case 'AWS::EC2::Subnet':
642
- return await this._checkSubnetDependencies(resource.physicalId);
643
- case 'AWS::EC2::SecurityGroup':
644
- return await this._checkSecurityGroupDependencies(resource.physicalId);
645
- default:
646
- return { hasBlockingDependencies: false, blocking: [], dependent: [] };
647
- }
648
- }
649
-
650
- /**
651
- * Check VPC dependencies
652
- * @private
653
- */
654
- async _checkVpcDependencies(vpcId) {
655
- const [subnets, securityGroups, natGateways, igws, endpoints] = await Promise.all([
656
- this.ec2.describeSubnets({ Filters: [{ Name: 'vpc-id', Values: [vpcId] }] }),
657
- this.ec2.describeSecurityGroups({ Filters: [{ Name: 'vpc-id', Values: [vpcId] }] }),
658
- this.ec2.describeNatGateways({ Filter: [{ Name: 'vpc-id', Values: [vpcId] }] }),
659
- this.ec2.describeInternetGateways({ Filters: [{ Name: 'attachment.vpc-id', Values: [vpcId] }] }),
660
- this.ec2.describeVpcEndpoints({ Filters: [{ Name: 'vpc-id', Values: [vpcId] }] }),
661
- ]);
662
-
663
- const blocking = [];
664
-
665
- // Filter out default resources that can be deleted with VPC
666
- const customSubnets = subnets.Subnets.filter(s => !s.DefaultForAz);
667
- const customSGs = securityGroups.SecurityGroups.filter(sg => sg.GroupName !== 'default');
668
-
669
- if (customSubnets.length > 0) {
670
- blocking.push({ type: 'subnets', count: customSubnets.length, ids: customSubnets.map(s => s.SubnetId) });
671
- }
672
- if (customSGs.length > 0) {
673
- blocking.push({ type: 'security_groups', count: customSGs.length, ids: customSGs.map(sg => sg.GroupId) });
674
- }
675
- if (natGateways.NatGateways?.length > 0) {
676
- blocking.push({ type: 'nat_gateways', count: natGateways.NatGateways.length });
677
- }
678
- if (igws.InternetGateways?.length > 0) {
679
- blocking.push({ type: 'internet_gateways', count: igws.InternetGateways.length });
680
- }
681
- if (endpoints.VpcEndpoints?.length > 0) {
682
- blocking.push({ type: 'vpc_endpoints', count: endpoints.VpcEndpoints.length });
683
- }
684
-
685
- return {
686
- hasBlockingDependencies: blocking.length > 0,
687
- blocking,
688
- dependent: [],
689
- };
690
- }
691
-
692
- /**
693
- * Check security group dependencies
694
- * @private
695
- */
696
- async _checkSecurityGroupDependencies(sgId) {
697
- // Check if security group is in use by any resources
698
- const [ec2Instances, rdsInstances, lambdaFunctions, loadBalancers] = await Promise.all([
699
- this.ec2.describeInstances({ Filters: [{ Name: 'instance.group-id', Values: [sgId] }] }),
700
- this.rds.describeDBInstances(),
701
- this.lambda.listFunctions(),
702
- this.elb.describeLoadBalancers(),
703
- ]);
704
-
705
- const blocking = [];
706
-
707
- // Check EC2 instances
708
- const instances = ec2Instances.Reservations.flatMap(r => r.Instances);
709
- if (instances.length > 0) {
710
- blocking.push({
711
- type: 'ec2_instances',
712
- count: instances.length,
713
- ids: instances.map(i => i.InstanceId),
714
- });
715
- }
716
-
717
- // Check RDS instances
718
- const rdsUsingThisSG = rdsInstances.DBInstances.filter(db =>
719
- db.VpcSecurityGroups?.some(sg => sg.VpcSecurityGroupId === sgId)
720
- );
721
- if (rdsUsingThisSG.length > 0) {
722
- blocking.push({
723
- type: 'rds_instances',
724
- count: rdsUsingThisSG.length,
725
- ids: rdsUsingThisSG.map(db => db.DBInstanceIdentifier),
726
- });
727
- }
728
-
729
- // Check Lambda functions
730
- const lambdaUsingThisSG = lambdaFunctions.Functions.filter(fn =>
731
- fn.VpcConfig?.SecurityGroupIds?.includes(sgId)
732
- );
733
- if (lambdaUsingThisSG.length > 0) {
734
- blocking.push({
735
- type: 'lambda_functions',
736
- count: lambdaUsingThisSG.length,
737
- ids: lambdaUsingThisSG.map(fn => fn.FunctionName),
738
- });
739
- }
740
-
741
- return {
742
- hasBlockingDependencies: blocking.length > 0,
743
- blocking,
744
- dependent: [],
745
- };
746
- }
747
-
748
- /**
749
- * Get deletion phase for resource type
750
- * @private
751
- */
752
- _getDeletionPhase(resourceType) {
753
- const phaseMap = {
754
- 'AWS::EC2::VPCEndpoint': 1,
755
- 'AWS::EC2::NatGateway': 2,
756
- 'AWS::EC2::InternetGateway': 2,
757
- 'AWS::EC2::RouteTable': 2,
758
- 'AWS::EC2::NetworkAcl': 2,
759
- 'AWS::EC2::Subnet': 2,
760
- 'AWS::EC2::SecurityGroup': 2,
761
- 'AWS::EC2::VPC': 3,
762
- };
763
-
764
- return phaseMap[resourceType] || 2;
765
- }
766
-
767
- /**
768
- * Compare deletion priority
769
- * @private
770
- */
771
- _compareDeletionPriority(a, b) {
772
- const priorityMap = {
773
- 'AWS::EC2::VPCEndpoint': 1,
774
- 'AWS::EC2::NatGateway': 2,
775
- 'AWS::EC2::InternetGateway': 3,
776
- 'AWS::EC2::RouteTable': 4,
777
- 'AWS::EC2::Subnet': 5,
778
- 'AWS::EC2::SecurityGroup': 6,
779
- 'AWS::EC2::VPC': 7,
780
- };
781
-
782
- return (priorityMap[a.resourceType] || 99) - (priorityMap[b.resourceType] || 99);
783
- }
784
- }
785
- ```
786
-
787
- #### 2. ResourceDeletionPlanner (NEW)
788
-
789
- **Location**: `packages/devtools/infrastructure/domains/health/domain/services/resource-deletion-planner.js`
790
-
791
- **Responsibilities**:
792
- - Create detailed deletion plan
793
- - Calculate cost savings
794
- - Generate warnings
795
-
796
- **Public Methods**:
797
-
798
- ```javascript
799
- class ResourceDeletionPlanner {
800
- /**
801
- * Create deletion plan with phases and cost estimates
802
- *
803
- * @param {Object} params
804
- * @param {Array} params.resources - Resources to delete
805
- * @param {Object} params.dependencyAnalysis - Dependency analysis result
806
- * @returns {Object} Deletion plan
807
- */
808
- createDeletionPlan({ resources, dependencyAnalysis }) {
809
- // 1. Filter out blocked resources
810
- const deletableResources = resources.filter(
811
- (r) => !dependencyAnalysis.blockedResources.some(
812
- (b) => b.resource.physicalId === r.physicalId
813
- )
814
- );
815
-
816
- // 2. Determine deletion order
817
- const deletionPhases = this._organizeDeletionPhases(deletableResources);
818
-
819
- // 3. Calculate cost savings
820
- const costSavings = this._calculateCostSavings(deletableResources);
821
-
822
- // 4. Generate warnings
823
- const warnings = this._generateWarnings(deletableResources, dependencyAnalysis);
824
-
825
- return {
826
- totalResources: resources.length,
827
- deletableCount: deletableResources.length,
828
- blockedCount: dependencyAnalysis.blockedResources.length,
829
- phases: deletionPhases,
830
- costSavings,
831
- warnings,
832
- blockedResources: dependencyAnalysis.blockedResources,
833
- };
834
- }
835
-
836
- /**
837
- * Calculate estimated monthly cost savings
838
- * @private
839
- */
840
- _calculateCostSavings(resources) {
841
- let monthlyCost = 0;
842
-
843
- for (const resource of resources) {
844
- switch (resource.resourceType) {
845
- case 'AWS::EC2::VPC':
846
- // Assume NAT Gateway cost (most expensive part of VPC)
847
- monthlyCost += 36; // $32.40 for NAT + $0.045/GB
848
- break;
849
- case 'AWS::EC2::NatGateway':
850
- monthlyCost += 36;
851
- break;
852
- case 'AWS::EC2::EIP':
853
- monthlyCost += 3.65; // $0.005/hour
854
- break;
855
- // Other resources have minimal direct costs
856
- }
857
- }
858
-
859
- return {
860
- monthly: monthlyCost,
861
- annual: monthlyCost * 12,
862
- };
863
- }
864
-
865
- /**
866
- * Generate warnings for deletion
867
- * @private
868
- */
869
- _generateWarnings(resources, dependencyAnalysis) {
870
- const warnings = [
871
- 'This operation cannot be easily undone',
872
- 'Resources will be permanently deleted from AWS',
873
- 'Verify no applications depend on these resources',
874
- ];
875
-
876
- // Add specific warnings based on resource types
877
- if (resources.some(r => r.resourceType === 'AWS::EC2::VPC')) {
878
- warnings.push('Deleting VPCs will also delete associated default resources');
879
- }
880
-
881
- if (dependencyAnalysis.blockedResources.length > 0) {
882
- warnings.push(
883
- `${dependencyAnalysis.blockedResources.length} resources cannot be deleted due to dependencies`
884
- );
885
- }
886
-
887
- return warnings;
888
- }
889
- }
890
- ```
891
-
892
- #### 3. CleanupOrphanedResourcesUseCase (NEW)
893
-
894
- **Location**: `packages/devtools/infrastructure/domains/health/application/use-cases/cleanup-orphaned-resources-use-case.js`
895
-
896
- **Responsibilities**:
897
- - Orchestrate complete cleanup workflow
898
- - Coordinate categorizer, analyzer, planner, and deleter
899
- - Handle errors and rollback
900
-
901
- **Public Methods**:
902
-
903
- ```javascript
904
- class CleanupOrphanedResourcesUseCase {
905
- constructor({
906
- orphanedResourceCategorizerService,
907
- resourceDependencyAnalyzer,
908
- resourceDeletionPlanner,
909
- resourceDeleterRepository,
910
- auditLogRepository,
911
- }) {
912
- this.categorizerService = orphanedResourceCategorizerService;
913
- this.dependencyAnalyzer = resourceDependencyAnalyzer;
914
- this.deletionPlanner = resourceDeletionPlanner;
915
- this.deleterRepo = resourceDeleterRepository;
916
- this.auditRepo = auditLogRepository;
917
- }
918
-
919
- /**
920
- * Execute cleanup workflow
921
- *
922
- * @param {Object} params
923
- * @param {StackIdentifier} params.stackIdentifier
924
- * @param {string} params.buildTemplatePath
925
- * @param {boolean} params.dryRun - If true, only plan, don't execute
926
- * @param {string} params.resourceTypeFilter - Optional resource type filter
927
- * @param {string} params.logicalIdPattern - Optional logical ID pattern
928
- * @returns {Promise<Object>} Cleanup result
929
- */
930
- async execute({
931
- stackIdentifier,
932
- buildTemplatePath,
933
- dryRun = true,
934
- resourceTypeFilter = null,
935
- logicalIdPattern = null,
936
- }) {
937
- // 1. Get duplicate orphaned resources
938
- const categorization = await this.categorizerService.categorize({
939
- orphanedResources: [], // Will be fetched internally
940
- stackIdentifier,
941
- buildTemplatePath,
942
- });
943
-
944
- let duplicates = categorization.duplicates || [];
945
-
946
- // 2. Apply filters
947
- if (resourceTypeFilter) {
948
- duplicates = duplicates.filter((r) => r.resourceType === resourceTypeFilter);
949
- }
950
- if (logicalIdPattern) {
951
- const regex = new RegExp(logicalIdPattern.replace('*', '.*'));
952
- duplicates = duplicates.filter((r) => regex.test(r.logicalId));
953
- }
954
-
955
- if (duplicates.length === 0) {
956
- return {
957
- success: true,
958
- message: 'No duplicate orphaned resources found',
959
- deletedCount: 0,
960
- skippedCount: 0,
961
- };
962
- }
963
-
964
- // 3. Analyze dependencies
965
- const dependencyAnalysis = await this.dependencyAnalyzer.analyzeDependencies(duplicates);
966
-
967
- // 4. Create deletion plan
968
- const deletionPlan = this.deletionPlanner.createDeletionPlan({
969
- resources: duplicates,
970
- dependencyAnalysis,
971
- });
972
-
973
- // 5. If dry-run, return plan without executing
974
- if (dryRun) {
975
- return {
976
- dryRun: true,
977
- deletionPlan,
978
- message: 'Dry-run complete. No resources were deleted.',
979
- };
980
- }
981
-
982
- // 6. Execute deletions
983
- const deletionResult = await this._executeDeletionPlan(
984
- deletionPlan,
985
- stackIdentifier
986
- );
987
-
988
- // 7. Log to audit trail
989
- await this.auditRepo.logCleanupOperation({
990
- stackIdentifier,
991
- deletionPlan,
992
- result: deletionResult,
993
- timestamp: new Date().toISOString(),
994
- });
995
-
996
- return {
997
- success: true,
998
- dryRun: false,
999
- deletedCount: deletionResult.successCount,
1000
- failedCount: deletionResult.failedCount,
1001
- skippedCount: deletionPlan.blockedCount,
1002
- deletionResult,
1003
- costSavings: deletionPlan.costSavings,
1004
- };
1005
- }
1006
-
1007
- /**
1008
- * Execute deletion plan phase by phase
1009
- * @private
1010
- */
1011
- async _executeDeletionPlan(deletionPlan, stackIdentifier) {
1012
- const results = {
1013
- successCount: 0,
1014
- failedCount: 0,
1015
- deleted: [],
1016
- failed: [],
1017
- };
1018
-
1019
- // Execute in order: phase1, phase2, phase3
1020
- for (const phase of ['phase1', 'phase2', 'phase3']) {
1021
- const resources = deletionPlan.phases[phase];
1022
-
1023
- for (const resource of resources) {
1024
- try {
1025
- await this.deleterRepo.deleteResource({
1026
- resourceType: resource.resourceType,
1027
- physicalId: resource.physicalId,
1028
- region: stackIdentifier.region,
1029
- });
1030
-
1031
- results.successCount++;
1032
- results.deleted.push({
1033
- physicalId: resource.physicalId,
1034
- resourceType: resource.resourceType,
1035
- });
1036
- } catch (error) {
1037
- results.failedCount++;
1038
- results.failed.push({
1039
- physicalId: resource.physicalId,
1040
- resourceType: resource.resourceType,
1041
- error: error.message,
1042
- });
1043
- }
1044
-
1045
- // Small delay between deletions to avoid throttling
1046
- await this._delay(500);
1047
- }
1048
-
1049
- // Delay between phases to allow AWS to clean up
1050
- if (resources.length > 0) {
1051
- await this._delay(5000);
1052
- }
1053
- }
1054
-
1055
- return results;
1056
- }
1057
-
1058
- _delay(ms) {
1059
- return new Promise((resolve) => setTimeout(resolve, ms));
1060
- }
1061
- }
1062
- ```
1063
-
1064
- #### 4. ResourceDeleterRepository (NEW)
1065
-
1066
- **Location**: `packages/devtools/infrastructure/domains/health/infrastructure/repositories/resource-deleter-repository.js`
1067
-
1068
- **Responsibilities**:
1069
- - Execute AWS API calls for resource deletion
1070
- - Handle AWS-specific errors
1071
-
1072
- **Public Methods**:
1073
-
1074
- ```javascript
1075
- class ResourceDeleterRepository {
1076
- constructor({ ec2Client, region }) {
1077
- this.ec2 = ec2Client;
1078
- this.region = region;
1079
- }
1080
-
1081
- /**
1082
- * Delete a resource from AWS
1083
- *
1084
- * @param {Object} params
1085
- * @param {string} params.resourceType - CloudFormation resource type
1086
- * @param {string} params.physicalId - Physical resource ID
1087
- * @param {string} params.region - AWS region
1088
- * @returns {Promise<Object>} Deletion result
1089
- */
1090
- async deleteResource({ resourceType, physicalId, region }) {
1091
- try {
1092
- switch (resourceType) {
1093
- case 'AWS::EC2::VPC':
1094
- return await this._deleteVpc(physicalId);
1095
- case 'AWS::EC2::Subnet':
1096
- return await this._deleteSubnet(physicalId);
1097
- case 'AWS::EC2::SecurityGroup':
1098
- return await this._deleteSecurityGroup(physicalId);
1099
- // Add more resource types as needed
1100
- default:
1101
- throw new Error(`Unsupported resource type: ${resourceType}`);
1102
- }
1103
- } catch (error) {
1104
- throw new Error(
1105
- `Failed to delete ${resourceType} ${physicalId}: ${error.message}`
1106
- );
1107
- }
1108
- }
1109
-
1110
- /**
1111
- * Delete VPC
1112
- * @private
1113
- */
1114
- async _deleteVpc(vpcId) {
1115
- await this.ec2.deleteVpc({ VpcId: vpcId });
1116
- return { success: true, physicalId: vpcId };
1117
- }
1118
-
1119
- /**
1120
- * Delete Subnet
1121
- * @private
1122
- */
1123
- async _deleteSubnet(subnetId) {
1124
- await this.ec2.deleteSubnet({ SubnetId: subnetId });
1125
- return { success: true, physicalId: subnetId };
1126
- }
1127
-
1128
- /**
1129
- * Delete Security Group
1130
- * @private
1131
- */
1132
- async _deleteSecurityGroup(sgId) {
1133
- await this.ec2.deleteSecurityGroup({ GroupId: sgId });
1134
- return { success: true, physicalId: sgId };
1135
- }
1136
- }
1137
- ```
1138
-
1139
- ### Data Flow
1140
-
1141
- ```
1142
- User runs: frigg cleanup --orphaned --execute
1143
-
1144
- cleanup-command/index.js
1145
- ↓ calls
1146
- CleanupOrphanedResourcesUseCase.execute()
1147
- ↓ calls
1148
- OrphanedResourceCategorizerService.categorize()
1149
- ↓ returns
1150
- { duplicates: [...] }
1151
-
1152
- ResourceDependencyAnalyzer.analyzeDependencies()
1153
- ↓ returns
1154
- { blockedResources: [...], dependencies: {...} }
1155
-
1156
- ResourceDeletionPlanner.createDeletionPlan()
1157
- ↓ returns
1158
- { phases: {...}, costSavings: {...}, warnings: [...] }
1159
-
1160
- Display confirmation prompt (unless --yes)
1161
- ↓ if confirmed
1162
- _executeDeletionPlan()
1163
- ↓ phase by phase
1164
- ResourceDeleterRepository.deleteResource()
1165
- ↓ for each resource
1166
- AWS API calls (deleteVpc, deleteSubnet, etc.)
1167
-
1168
- AuditLogRepository.logCleanupOperation()
1169
-
1170
- Display result summary
1171
- ```
1172
-
1173
- ## Implementation Plan
1174
-
1175
- ### Phase 1: Core Dependency Analysis (TDD)
1176
-
1177
- 1. **Write tests** for `ResourceDependencyAnalyzer`
1178
- - Test VPC dependency checking
1179
- - Test security group dependency checking
1180
- - Test deletion order determination
1181
- - Test blocking dependency detection
1182
-
1183
- 2. **Implement** `ResourceDependencyAnalyzer`
1184
- - Implement dependency checking methods
1185
- - Implement deletion order logic
1186
- - Handle AWS API calls
1187
-
1188
- 3. **Verify** tests pass
1189
-
1190
- ### Phase 2: Deletion Planning (TDD)
1191
-
1192
- 1. **Write tests** for `ResourceDeletionPlanner`
1193
- - Test deletion plan creation
1194
- - Test cost calculation
1195
- - Test warning generation
1196
- - Test phase organization
1197
-
1198
- 2. **Implement** `ResourceDeletionPlanner`
1199
- - Implement plan creation
1200
- - Implement cost calculator
1201
- - Implement warning generator
1202
-
1203
- 3. **Verify** tests pass
1204
-
1205
- ### Phase 3: Use Case Orchestration (TDD)
1206
-
1207
- 1. **Write tests** for `CleanupOrphanedResourcesUseCase`
1208
- - Test dry-run mode
1209
- - Test execute mode
1210
- - Test filtering (resource type, logical ID)
1211
- - Test error handling
1212
- - Test rollback behavior
1213
-
1214
- 2. **Implement** use case
1215
- - Orchestrate all services
1216
- - Implement deletion execution
1217
- - Handle errors gracefully
1218
-
1219
- 3. **Verify** tests pass
1220
-
1221
- ### Phase 4: Infrastructure Repository (TDD)
1222
-
1223
- 1. **Write tests** for `ResourceDeleterRepository`
1224
- - Test VPC deletion
1225
- - Test subnet deletion
1226
- - Test security group deletion
1227
- - Test error handling
1228
-
1229
- 2. **Implement** repository
1230
- - AWS SDK integration
1231
- - Error handling and retries
1232
-
1233
- 3. **Verify** tests pass
1234
-
1235
- ### Phase 5: CLI Integration
1236
-
1237
- 1. **Create** `cleanup-command/index.js`
1238
- - Command line parsing
1239
- - Confirmation prompts
1240
- - Output formatting
1241
- - Progress indicators
1242
-
1243
- 2. **Test** with real data
1244
- - Test dry-run mode
1245
- - Test execute mode
1246
- - Test error scenarios
1247
- - Test with `quo-integrations-dev`
1248
-
1249
- 3. **Update documentation**
1250
-
1251
- ## Testing Strategy
1252
-
1253
- ### Unit Tests
1254
-
1255
- **ResourceDependencyAnalyzer**:
1256
- - ✅ Analyze VPC with dependencies
1257
- - ✅ Analyze VPC without dependencies
1258
- - ✅ Analyze security group in use
1259
- - ✅ Analyze security group not in use
1260
- - ✅ Determine deletion order correctly
1261
- - ✅ Handle API errors gracefully
1262
-
1263
- **ResourceDeletionPlanner**:
1264
- - ✅ Create plan with multiple phases
1265
- - ✅ Calculate cost savings accurately
1266
- - ✅ Generate appropriate warnings
1267
- - ✅ Handle blocked resources
1268
- - ✅ Filter resources correctly
1269
-
1270
- **CleanupOrphanedResourcesUseCase**:
1271
- - ✅ Dry-run returns plan without deleting
1272
- - ✅ Execute mode deletes resources
1273
- - ✅ Stop on first error (optional behavior)
1274
- - ✅ Continue on non-fatal errors
1275
- - ✅ Filter by resource type
1276
- - ✅ Filter by logical ID pattern
1277
- - ✅ Log to audit trail
1278
-
1279
- **ResourceDeleterRepository**:
1280
- - ✅ Delete VPC successfully
1281
- - ✅ Delete subnet successfully
1282
- - ✅ Delete security group successfully
1283
- - ✅ Handle dependency errors
1284
- - ✅ Handle permission errors
1285
- - ✅ Retry on throttling
1286
-
1287
- ### Integration Tests
1288
-
1289
- - ✅ End-to-end cleanup with mock AWS data
1290
- - ✅ Verify deletion order is correct
1291
- - ✅ Verify blocked resources are not deleted
1292
- - ✅ Verify audit log is written
1293
- - ✅ Test with `quo-integrations-dev` stack (11 duplicate resources)
1294
-
1295
- ### Real-World Validation
1296
-
1297
- Use `quo-integrations-dev` stack as reference:
1298
- - 11 duplicate orphaned resources
1299
- - 2 VPCs with dependencies (9 total)
1300
- - Verify all dependencies are detected
1301
- - Verify deletion plan is safe
1302
- - Execute in isolated test environment
1303
-
1304
- ## Security Considerations
1305
-
1306
- ### 1. Permission Requirements
1307
-
1308
- **Minimum IAM Policy**:
1309
-
1310
- ```json
1311
- {
1312
- "Version": "2012-10-17",
1313
- "Statement": [
1314
- {
1315
- "Effect": "Allow",
1316
- "Action": [
1317
- "ec2:DeleteVpc",
1318
- "ec2:DeleteSubnet",
1319
- "ec2:DeleteSecurityGroup",
1320
- "ec2:DeleteInternetGateway",
1321
- "ec2:DeleteNatGateway",
1322
- "ec2:DeleteRouteTable",
1323
- "ec2:DeleteVpcEndpoint",
1324
- "ec2:DescribeVpcs",
1325
- "ec2:DescribeSubnets",
1326
- "ec2:DescribeSecurityGroups",
1327
- "ec2:DescribeInstances",
1328
- "ec2:DescribeNatGateways",
1329
- "ec2:DescribeInternetGateways",
1330
- "ec2:DescribeVpcEndpoints"
1331
- ],
1332
- "Resource": "*",
1333
- "Condition": {
1334
- "StringEquals": {
1335
- "ec2:ResourceTag/ManagedBy": "Frigg"
1336
- }
1337
- }
1338
- }
1339
- ]
1340
- }
1341
- ```
1342
-
1343
- ### 2. Audit Logging
1344
-
1345
- All cleanup operations must be logged:
1346
-
1347
- ```javascript
1348
- {
1349
- "operation": "cleanup_orphaned_resources",
1350
- "timestamp": "2025-10-27T10:30:00Z",
1351
- "user": "user@example.com",
1352
- "stackName": "quo-integrations-dev",
1353
- "region": "us-east-1",
1354
- "mode": "execute",
1355
- "resources": {
1356
- "total": 11,
1357
- "deleted": 11,
1358
- "failed": 0
1359
- },
1360
- "deletedResources": [
1361
- {
1362
- "physicalId": "vpc-0e2351eac99adcb83",
1363
- "resourceType": "AWS::EC2::VPC",
1364
- "logicalId": "FriggVPC",
1365
- "deletedAt": "2025-10-27T10:30:15Z"
1366
- }
1367
- // ... 10 more
1368
- ],
1369
- "costSavings": {
1370
- "monthly": 72.00,
1371
- "annual": 864.00
1372
- }
1373
- }
1374
- ```
1375
-
1376
- ### 3. Safety Mechanisms
1377
-
1378
- - ✅ Dry-run by default
1379
- - ✅ Explicit confirmation required
1380
- - ✅ Dependency checking before deletion
1381
- - ✅ Deletion order enforcement
1382
- - ✅ Audit trail for all operations
1383
- - ✅ Support for --yes flag (automation)
1384
- - ✅ Permission checks before deletion
1385
- - ✅ Resource tagging verification (only delete Frigg-managed resources)
1386
-
1387
- ## Success Criteria
1388
-
1389
- 1. ✅ Command correctly identifies duplicate orphaned resources
1390
- 2. ✅ Dependency analysis detects blocking dependencies
1391
- 3. ✅ Deletion plan orders resources correctly
1392
- 4. ✅ Dry-run mode shows plan without deleting
1393
- 5. ✅ Execute mode deletes resources safely
1394
- 6. ✅ Error handling is robust (permissions, dependencies, API errors)
1395
- 7. ✅ All deletions are logged to audit trail
1396
- 8. ✅ Cost savings are calculated accurately
1397
- 9. ✅ All tests pass (unit, integration, real-world validation)
1398
- 10. ✅ Documentation is complete
1399
-
1400
- ## Open Questions
1401
-
1402
- 1. **Rollback strategy**: Should we support rollback if deletion fails partway through? (Complex: resources can't be "undeleted")
1403
- 2. **Backup before delete**: Should we export resource configurations before deletion for recovery purposes?
1404
- 3. **Concurrent deletion**: Should we delete independent resources in parallel for speed?
1405
- 4. **Retry policy**: How many retries for throttling errors? What backoff strategy?
1406
- 5. **Confirmation format**: Is `delete <stack-name>` sufficient, or should we require typing the exact resource count?
1407
- 6. **Cost estimation**: Should we call AWS Pricing API for accurate costs, or use hardcoded estimates?
1408
-
1409
- ## Related Specifications
1410
-
1411
- - [SPEC-ENHANCED-HEALTH-REPORT.md](./SPEC-ENHANCED-HEALTH-REPORT.md) - Enhanced health report with resource categorization
1412
- - [FIX-SUMMARY.md](../debug/FIX-SUMMARY.md) - Root cause analysis and fixes for logical ID mapping
1413
-
1414
- ## References
1415
-
1416
- - User testing with `quo-integrations-dev` stack (11 duplicate resources)
1417
- - Real-world data documented in `ACTUAL-DATA-SHAPE.md`
1418
- - AWS EC2 API documentation for resource deletion
1419
- - CloudFormation resource import documentation