@friggframework/devtools 2.0.0-next.45 → 2.0.0-next.47
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/infrastructure/ARCHITECTURE.md +487 -0
- package/infrastructure/HEALTH.md +468 -0
- package/infrastructure/README.md +51 -0
- package/infrastructure/__tests__/postgres-config.test.js +914 -0
- package/infrastructure/__tests__/template-generation.test.js +687 -0
- package/infrastructure/create-frigg-infrastructure.js +1 -1
- package/infrastructure/docs/POSTGRES-CONFIGURATION.md +630 -0
- package/infrastructure/{DEPLOYMENT-INSTRUCTIONS.md → docs/deployment-instructions.md} +3 -3
- package/infrastructure/{IAM-POLICY-TEMPLATES.md → docs/iam-policy-templates.md} +9 -10
- package/infrastructure/domains/database/aurora-builder.js +809 -0
- package/infrastructure/domains/database/aurora-builder.test.js +950 -0
- package/infrastructure/domains/database/aurora-discovery.js +87 -0
- package/infrastructure/domains/database/aurora-discovery.test.js +188 -0
- package/infrastructure/domains/database/aurora-resolver.js +210 -0
- package/infrastructure/domains/database/aurora-resolver.test.js +347 -0
- package/infrastructure/domains/database/migration-builder.js +695 -0
- package/infrastructure/domains/database/migration-builder.test.js +294 -0
- package/infrastructure/domains/database/migration-resolver.js +163 -0
- package/infrastructure/domains/database/migration-resolver.test.js +337 -0
- package/infrastructure/domains/health/application/ports/IPropertyReconciler.js +164 -0
- package/infrastructure/domains/health/application/ports/IResourceDetector.js +129 -0
- package/infrastructure/domains/health/application/ports/IResourceImporter.js +142 -0
- package/infrastructure/domains/health/application/ports/IStackRepository.js +131 -0
- package/infrastructure/domains/health/application/ports/index.js +26 -0
- package/infrastructure/domains/health/application/use-cases/__tests__/execute-resource-import-use-case.test.js +679 -0
- package/infrastructure/domains/health/application/use-cases/__tests__/mismatch-analyzer-method-name.test.js +167 -0
- package/infrastructure/domains/health/application/use-cases/__tests__/repair-via-import-use-case.test.js +1130 -0
- package/infrastructure/domains/health/application/use-cases/execute-resource-import-use-case.js +221 -0
- package/infrastructure/domains/health/application/use-cases/reconcile-properties-use-case.js +152 -0
- package/infrastructure/domains/health/application/use-cases/reconcile-properties-use-case.test.js +343 -0
- package/infrastructure/domains/health/application/use-cases/repair-via-import-use-case.js +535 -0
- package/infrastructure/domains/health/application/use-cases/repair-via-import-use-case.test.js +376 -0
- package/infrastructure/domains/health/application/use-cases/run-health-check-use-case.js +213 -0
- package/infrastructure/domains/health/application/use-cases/run-health-check-use-case.test.js +441 -0
- package/infrastructure/domains/health/docs/ACME-DEV-DRIFT-ANALYSIS.md +267 -0
- package/infrastructure/domains/health/docs/BUILD-VS-DEPLOYED-TEMPLATE-ANALYSIS.md +324 -0
- package/infrastructure/domains/health/docs/ORPHAN-DETECTION-ANALYSIS.md +386 -0
- package/infrastructure/domains/health/docs/SPEC-CLEANUP-COMMAND.md +1419 -0
- package/infrastructure/domains/health/docs/TDD-IMPLEMENTATION-SUMMARY.md +391 -0
- package/infrastructure/domains/health/docs/TEMPLATE-COMPARISON-IMPLEMENTATION.md +551 -0
- package/infrastructure/domains/health/domain/entities/issue.js +299 -0
- package/infrastructure/domains/health/domain/entities/issue.test.js +528 -0
- package/infrastructure/domains/health/domain/entities/property-mismatch.js +108 -0
- package/infrastructure/domains/health/domain/entities/property-mismatch.test.js +275 -0
- package/infrastructure/domains/health/domain/entities/resource.js +159 -0
- package/infrastructure/domains/health/domain/entities/resource.test.js +432 -0
- package/infrastructure/domains/health/domain/entities/stack-health-report.js +306 -0
- package/infrastructure/domains/health/domain/entities/stack-health-report.test.js +601 -0
- package/infrastructure/domains/health/domain/services/__tests__/health-score-percentage-based.test.js +380 -0
- package/infrastructure/domains/health/domain/services/__tests__/import-progress-monitor.test.js +971 -0
- package/infrastructure/domains/health/domain/services/__tests__/import-template-generator.test.js +1150 -0
- package/infrastructure/domains/health/domain/services/__tests__/logical-id-mapper.test.js +672 -0
- package/infrastructure/domains/health/domain/services/__tests__/template-parser.test.js +496 -0
- package/infrastructure/domains/health/domain/services/__tests__/update-progress-monitor.test.js +419 -0
- package/infrastructure/domains/health/domain/services/health-score-calculator.js +248 -0
- package/infrastructure/domains/health/domain/services/health-score-calculator.test.js +504 -0
- package/infrastructure/domains/health/domain/services/import-progress-monitor.js +195 -0
- package/infrastructure/domains/health/domain/services/import-template-generator.js +435 -0
- package/infrastructure/domains/health/domain/services/logical-id-mapper.js +345 -0
- package/infrastructure/domains/health/domain/services/mismatch-analyzer.js +234 -0
- package/infrastructure/domains/health/domain/services/mismatch-analyzer.test.js +431 -0
- package/infrastructure/domains/health/domain/services/property-mutability-config.js +382 -0
- package/infrastructure/domains/health/domain/services/template-parser.js +245 -0
- package/infrastructure/domains/health/domain/services/update-progress-monitor.js +192 -0
- package/infrastructure/domains/health/domain/value-objects/health-score.js +138 -0
- package/infrastructure/domains/health/domain/value-objects/health-score.test.js +267 -0
- package/infrastructure/domains/health/domain/value-objects/property-mutability.js +161 -0
- package/infrastructure/domains/health/domain/value-objects/property-mutability.test.js +198 -0
- package/infrastructure/domains/health/domain/value-objects/resource-state.js +167 -0
- package/infrastructure/domains/health/domain/value-objects/resource-state.test.js +196 -0
- package/infrastructure/domains/health/domain/value-objects/stack-identifier.js +192 -0
- package/infrastructure/domains/health/domain/value-objects/stack-identifier.test.js +262 -0
- package/infrastructure/domains/health/infrastructure/adapters/__tests__/orphan-detection-cfn-tagged.test.js +312 -0
- package/infrastructure/domains/health/infrastructure/adapters/__tests__/orphan-detection-multi-stack.test.js +367 -0
- package/infrastructure/domains/health/infrastructure/adapters/__tests__/orphan-detection-relationship-analysis.test.js +432 -0
- package/infrastructure/domains/health/infrastructure/adapters/aws-property-reconciler.js +784 -0
- package/infrastructure/domains/health/infrastructure/adapters/aws-property-reconciler.test.js +1133 -0
- package/infrastructure/domains/health/infrastructure/adapters/aws-resource-detector.js +565 -0
- package/infrastructure/domains/health/infrastructure/adapters/aws-resource-detector.test.js +554 -0
- package/infrastructure/domains/health/infrastructure/adapters/aws-resource-importer.js +318 -0
- package/infrastructure/domains/health/infrastructure/adapters/aws-resource-importer.test.js +398 -0
- package/infrastructure/domains/health/infrastructure/adapters/aws-stack-repository.js +777 -0
- package/infrastructure/domains/health/infrastructure/adapters/aws-stack-repository.test.js +580 -0
- package/infrastructure/domains/integration/integration-builder.js +397 -0
- package/infrastructure/domains/integration/integration-builder.test.js +593 -0
- package/infrastructure/domains/integration/integration-resolver.js +170 -0
- package/infrastructure/domains/integration/integration-resolver.test.js +369 -0
- package/infrastructure/domains/integration/websocket-builder.js +69 -0
- package/infrastructure/domains/integration/websocket-builder.test.js +195 -0
- package/infrastructure/domains/networking/vpc-builder.js +1829 -0
- package/infrastructure/domains/networking/vpc-builder.test.js +1262 -0
- package/infrastructure/domains/networking/vpc-discovery.js +177 -0
- package/infrastructure/domains/networking/vpc-discovery.test.js +350 -0
- package/infrastructure/domains/networking/vpc-resolver.js +324 -0
- package/infrastructure/domains/networking/vpc-resolver.test.js +501 -0
- package/infrastructure/domains/parameters/ssm-builder.js +79 -0
- package/infrastructure/domains/parameters/ssm-builder.test.js +189 -0
- package/infrastructure/domains/parameters/ssm-discovery.js +84 -0
- package/infrastructure/domains/parameters/ssm-discovery.test.js +210 -0
- package/infrastructure/{iam-generator.js → domains/security/iam-generator.js} +2 -2
- package/infrastructure/domains/security/kms-builder.js +366 -0
- package/infrastructure/domains/security/kms-builder.test.js +374 -0
- package/infrastructure/domains/security/kms-discovery.js +80 -0
- package/infrastructure/domains/security/kms-discovery.test.js +177 -0
- package/infrastructure/domains/security/kms-resolver.js +96 -0
- package/infrastructure/domains/security/kms-resolver.test.js +216 -0
- package/infrastructure/domains/shared/base-builder.js +112 -0
- package/infrastructure/domains/shared/base-resolver.js +186 -0
- package/infrastructure/domains/shared/base-resolver.test.js +305 -0
- package/infrastructure/domains/shared/builder-orchestrator.js +212 -0
- package/infrastructure/domains/shared/builder-orchestrator.test.js +213 -0
- package/infrastructure/domains/shared/cloudformation-discovery-v2.js +334 -0
- package/infrastructure/domains/shared/cloudformation-discovery.js +375 -0
- package/infrastructure/domains/shared/cloudformation-discovery.test.js +590 -0
- package/infrastructure/domains/shared/environment-builder.js +119 -0
- package/infrastructure/domains/shared/environment-builder.test.js +247 -0
- package/infrastructure/domains/shared/providers/aws-provider-adapter.js +544 -0
- package/infrastructure/domains/shared/providers/aws-provider-adapter.test.js +377 -0
- package/infrastructure/domains/shared/providers/azure-provider-adapter.stub.js +93 -0
- package/infrastructure/domains/shared/providers/cloud-provider-adapter.js +136 -0
- package/infrastructure/domains/shared/providers/gcp-provider-adapter.stub.js +82 -0
- package/infrastructure/domains/shared/providers/provider-factory.js +108 -0
- package/infrastructure/domains/shared/providers/provider-factory.test.js +170 -0
- package/infrastructure/domains/shared/resource-discovery.js +192 -0
- package/infrastructure/domains/shared/resource-discovery.test.js +552 -0
- package/infrastructure/domains/shared/types/app-definition.js +205 -0
- package/infrastructure/domains/shared/types/discovery-result.js +106 -0
- package/infrastructure/domains/shared/types/discovery-result.test.js +258 -0
- package/infrastructure/domains/shared/types/index.js +46 -0
- package/infrastructure/domains/shared/types/resource-ownership.js +108 -0
- package/infrastructure/domains/shared/types/resource-ownership.test.js +101 -0
- package/infrastructure/domains/shared/utilities/base-definition-factory.js +380 -0
- package/infrastructure/domains/shared/utilities/base-definition-factory.js.bak +338 -0
- package/infrastructure/domains/shared/utilities/base-definition-factory.test.js +248 -0
- package/infrastructure/domains/shared/utilities/handler-path-resolver.js +134 -0
- package/infrastructure/domains/shared/utilities/handler-path-resolver.test.js +268 -0
- package/infrastructure/domains/shared/utilities/prisma-layer-manager.js +55 -0
- package/infrastructure/domains/shared/utilities/prisma-layer-manager.test.js +138 -0
- package/infrastructure/{env-validator.js → domains/shared/validation/env-validator.js} +2 -1
- package/infrastructure/domains/shared/validation/env-validator.test.js +173 -0
- package/infrastructure/esbuild.config.js +53 -0
- package/infrastructure/infrastructure-composer.js +87 -0
- package/infrastructure/{serverless-template.test.js → infrastructure-composer.test.js} +115 -24
- package/infrastructure/scripts/build-prisma-layer.js +553 -0
- package/infrastructure/scripts/build-prisma-layer.test.js +102 -0
- package/infrastructure/{build-time-discovery.js → scripts/build-time-discovery.js} +80 -48
- package/infrastructure/{build-time-discovery.test.js → scripts/build-time-discovery.test.js} +5 -4
- package/layers/prisma/nodejs/package.json +8 -0
- package/management-ui/server/utils/cliIntegration.js +1 -1
- package/management-ui/server/utils/environment/awsParameterStore.js +29 -18
- package/package.json +11 -11
- package/frigg-cli/.eslintrc.js +0 -141
- package/frigg-cli/__tests__/unit/commands/build.test.js +0 -251
- package/frigg-cli/__tests__/unit/commands/db-setup.test.js +0 -548
- package/frigg-cli/__tests__/unit/commands/install.test.js +0 -400
- package/frigg-cli/__tests__/unit/commands/ui.test.js +0 -346
- package/frigg-cli/__tests__/unit/utils/database-validator.test.js +0 -366
- package/frigg-cli/__tests__/unit/utils/error-messages.test.js +0 -304
- package/frigg-cli/__tests__/unit/utils/prisma-runner.test.js +0 -486
- package/frigg-cli/__tests__/utils/mock-factory.js +0 -270
- package/frigg-cli/__tests__/utils/prisma-mock.js +0 -194
- package/frigg-cli/__tests__/utils/test-fixtures.js +0 -463
- package/frigg-cli/__tests__/utils/test-setup.js +0 -287
- package/frigg-cli/build-command/index.js +0 -65
- package/frigg-cli/db-setup-command/index.js +0 -193
- package/frigg-cli/deploy-command/index.js +0 -175
- package/frigg-cli/generate-command/__tests__/generate-command.test.js +0 -301
- package/frigg-cli/generate-command/azure-generator.js +0 -43
- package/frigg-cli/generate-command/gcp-generator.js +0 -47
- package/frigg-cli/generate-command/index.js +0 -332
- package/frigg-cli/generate-command/terraform-generator.js +0 -555
- package/frigg-cli/generate-iam-command.js +0 -118
- package/frigg-cli/index.js +0 -75
- package/frigg-cli/index.test.js +0 -158
- package/frigg-cli/init-command/backend-first-handler.js +0 -756
- package/frigg-cli/init-command/index.js +0 -93
- package/frigg-cli/init-command/template-handler.js +0 -143
- package/frigg-cli/install-command/backend-js.js +0 -33
- package/frigg-cli/install-command/commit-changes.js +0 -16
- package/frigg-cli/install-command/environment-variables.js +0 -127
- package/frigg-cli/install-command/environment-variables.test.js +0 -136
- package/frigg-cli/install-command/index.js +0 -54
- package/frigg-cli/install-command/install-package.js +0 -13
- package/frigg-cli/install-command/integration-file.js +0 -30
- package/frigg-cli/install-command/logger.js +0 -12
- package/frigg-cli/install-command/template.js +0 -90
- package/frigg-cli/install-command/validate-package.js +0 -75
- package/frigg-cli/jest.config.js +0 -124
- package/frigg-cli/package.json +0 -54
- package/frigg-cli/start-command/index.js +0 -149
- package/frigg-cli/start-command/start-command.test.js +0 -297
- package/frigg-cli/test/init-command.test.js +0 -180
- package/frigg-cli/test/npm-registry.test.js +0 -319
- package/frigg-cli/ui-command/index.js +0 -154
- package/frigg-cli/utils/app-resolver.js +0 -319
- package/frigg-cli/utils/backend-path.js +0 -25
- package/frigg-cli/utils/database-validator.js +0 -161
- package/frigg-cli/utils/error-messages.js +0 -257
- package/frigg-cli/utils/npm-registry.js +0 -167
- package/frigg-cli/utils/prisma-runner.js +0 -280
- package/frigg-cli/utils/process-manager.js +0 -199
- package/frigg-cli/utils/repo-detection.js +0 -405
- package/infrastructure/aws-discovery.js +0 -1176
- package/infrastructure/aws-discovery.test.js +0 -1220
- package/infrastructure/serverless-template.js +0 -2094
- /package/infrastructure/{WEBSOCKET-CONFIGURATION.md → docs/WEBSOCKET-CONFIGURATION.md} +0 -0
- /package/infrastructure/{GENERATE-IAM-DOCS.md → docs/generate-iam-command.md} +0 -0
- /package/infrastructure/{iam-generator.test.js → domains/security/iam-generator.test.js} +0 -0
- /package/infrastructure/{frigg-deployment-iam-stack.yaml → domains/security/templates/frigg-deployment-iam-stack.yaml} +0 -0
- /package/infrastructure/{iam-policy-basic.json → domains/security/templates/iam-policy-basic.json} +0 -0
- /package/infrastructure/{iam-policy-full.json → domains/security/templates/iam-policy-full.json} +0 -0
- /package/infrastructure/{run-discovery.js → scripts/run-discovery.js} +0 -0
|
@@ -0,0 +1,382 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Property Mutability Configuration
|
|
3
|
+
*
|
|
4
|
+
* Defines which CloudFormation resource properties are immutable (require replacement),
|
|
5
|
+
* mutable (can be updated), or conditional (depends on other properties).
|
|
6
|
+
*
|
|
7
|
+
* Based on AWS CloudFormation documentation "Update requires" behavior:
|
|
8
|
+
* - IMMUTABLE: "Replacement" - Cannot be changed without replacing the resource
|
|
9
|
+
* - MUTABLE: "No interruption" or "Some interruptions" - Can be updated in place
|
|
10
|
+
* - CONDITIONAL: Depends on other property values or specific conditions
|
|
11
|
+
*
|
|
12
|
+
* References:
|
|
13
|
+
* - AWS CloudFormation Template Reference: https://docs.aws.amazon.com/AWSCloudFormation/latest/TemplateReference/
|
|
14
|
+
* - Each resource type has "Update requires" documentation for each property
|
|
15
|
+
*/
|
|
16
|
+
|
|
17
|
+
const PropertyMutability = require('../value-objects/property-mutability');
|
|
18
|
+
|
|
19
|
+
/**
|
|
20
|
+
* Property mutability configuration by resource type
|
|
21
|
+
*
|
|
22
|
+
* Key: CloudFormation resource type (e.g., 'AWS::Lambda::Function')
|
|
23
|
+
* Value: Object mapping property paths to PropertyMutability instances
|
|
24
|
+
*
|
|
25
|
+
* Property paths match AWS drift detection format (without 'Properties.' prefix):
|
|
26
|
+
* - Simple: 'BucketName'
|
|
27
|
+
* - Nested: 'VpcConfig.SubnetIds'
|
|
28
|
+
*/
|
|
29
|
+
const PROPERTY_MUTABILITY_CONFIG = {
|
|
30
|
+
//
|
|
31
|
+
// AWS::EC2::* Resources
|
|
32
|
+
//
|
|
33
|
+
|
|
34
|
+
'AWS::EC2::VPC': {
|
|
35
|
+
// Immutable properties
|
|
36
|
+
'CidrBlock': PropertyMutability.IMMUTABLE, // Replacement required
|
|
37
|
+
'InstanceTenancy': PropertyMutability.IMMUTABLE, // Replacement required
|
|
38
|
+
|
|
39
|
+
// Mutable properties
|
|
40
|
+
'EnableDnsSupport': PropertyMutability.MUTABLE, // No interruption
|
|
41
|
+
'EnableDnsHostnames': PropertyMutability.MUTABLE, // No interruption
|
|
42
|
+
'Tags': PropertyMutability.MUTABLE, // No interruption
|
|
43
|
+
},
|
|
44
|
+
|
|
45
|
+
'AWS::EC2::Subnet': {
|
|
46
|
+
// Immutable properties
|
|
47
|
+
'VpcId': PropertyMutability.IMMUTABLE, // Replacement required
|
|
48
|
+
'CidrBlock': PropertyMutability.IMMUTABLE, // Replacement required
|
|
49
|
+
'AvailabilityZone': PropertyMutability.IMMUTABLE, // Replacement required
|
|
50
|
+
'AvailabilityZoneId': PropertyMutability.IMMUTABLE, // Replacement required
|
|
51
|
+
'Ipv4IpamPoolId': PropertyMutability.IMMUTABLE, // Replacement required
|
|
52
|
+
'Ipv4NetmaskLength': PropertyMutability.IMMUTABLE, // Replacement required
|
|
53
|
+
'Ipv6IpamPoolId': PropertyMutability.IMMUTABLE, // Replacement required
|
|
54
|
+
'Ipv6Native': PropertyMutability.IMMUTABLE, // Replacement required
|
|
55
|
+
'Ipv6NetmaskLength': PropertyMutability.IMMUTABLE, // Replacement required
|
|
56
|
+
'OutpostArn': PropertyMutability.IMMUTABLE, // Replacement required
|
|
57
|
+
|
|
58
|
+
// Mutable properties
|
|
59
|
+
'AssignIpv6AddressOnCreation': PropertyMutability.MUTABLE, // No interruption
|
|
60
|
+
'EnableDns64': PropertyMutability.MUTABLE, // No interruption
|
|
61
|
+
'EnableLniAtDeviceIndex': PropertyMutability.MUTABLE, // No interruption
|
|
62
|
+
'Ipv6CidrBlock': PropertyMutability.MUTABLE, // Some interruptions
|
|
63
|
+
'MapPublicIpOnLaunch': PropertyMutability.MUTABLE, // No interruption
|
|
64
|
+
'PrivateDnsNameOptionsOnLaunch': PropertyMutability.MUTABLE, // No interruption
|
|
65
|
+
'Tags': PropertyMutability.MUTABLE, // No interruption
|
|
66
|
+
},
|
|
67
|
+
|
|
68
|
+
'AWS::EC2::SecurityGroup': {
|
|
69
|
+
// Immutable properties
|
|
70
|
+
'VpcId': PropertyMutability.IMMUTABLE, // Replacement required
|
|
71
|
+
'GroupName': PropertyMutability.IMMUTABLE, // Replacement required
|
|
72
|
+
|
|
73
|
+
// Mutable properties
|
|
74
|
+
'GroupDescription': PropertyMutability.MUTABLE, // No interruption
|
|
75
|
+
'SecurityGroupIngress': PropertyMutability.MUTABLE, // No interruption
|
|
76
|
+
'SecurityGroupEgress': PropertyMutability.MUTABLE, // No interruption
|
|
77
|
+
'Tags': PropertyMutability.MUTABLE, // No interruption
|
|
78
|
+
},
|
|
79
|
+
|
|
80
|
+
'AWS::EC2::RouteTable': {
|
|
81
|
+
// Immutable properties
|
|
82
|
+
'VpcId': PropertyMutability.IMMUTABLE, // Replacement required
|
|
83
|
+
|
|
84
|
+
// Mutable properties
|
|
85
|
+
'Tags': PropertyMutability.MUTABLE, // No interruption
|
|
86
|
+
},
|
|
87
|
+
|
|
88
|
+
'AWS::EC2::Instance': {
|
|
89
|
+
// Immutable properties
|
|
90
|
+
'ImageId': PropertyMutability.IMMUTABLE, // Replacement required
|
|
91
|
+
'InstanceType': PropertyMutability.IMMUTABLE, // Replacement required
|
|
92
|
+
'KeyName': PropertyMutability.IMMUTABLE, // Replacement required
|
|
93
|
+
'AvailabilityZone': PropertyMutability.IMMUTABLE, // Replacement required
|
|
94
|
+
'PlacementGroupName': PropertyMutability.IMMUTABLE, // Replacement required
|
|
95
|
+
'PrivateIpAddress': PropertyMutability.IMMUTABLE, // Replacement required
|
|
96
|
+
'SubnetId': PropertyMutability.IMMUTABLE, // Replacement required
|
|
97
|
+
|
|
98
|
+
// Mutable properties
|
|
99
|
+
'SecurityGroupIds': PropertyMutability.MUTABLE, // No interruption (VPC instances)
|
|
100
|
+
'SecurityGroups': PropertyMutability.MUTABLE, // No interruption
|
|
101
|
+
'UserData': PropertyMutability.MUTABLE, // Some interruptions
|
|
102
|
+
'IamInstanceProfile': PropertyMutability.MUTABLE, // No interruption
|
|
103
|
+
'Monitoring': PropertyMutability.MUTABLE, // No interruption
|
|
104
|
+
'Tags': PropertyMutability.MUTABLE, // No interruption
|
|
105
|
+
'DisableApiTermination': PropertyMutability.MUTABLE, // No interruption
|
|
106
|
+
'EbsOptimized': PropertyMutability.MUTABLE, // Some interruptions
|
|
107
|
+
},
|
|
108
|
+
|
|
109
|
+
//
|
|
110
|
+
// AWS::Lambda::* Resources
|
|
111
|
+
//
|
|
112
|
+
|
|
113
|
+
'AWS::Lambda::Function': {
|
|
114
|
+
// Immutable properties
|
|
115
|
+
'FunctionName': PropertyMutability.IMMUTABLE, // Replacement required
|
|
116
|
+
|
|
117
|
+
// Mutable properties
|
|
118
|
+
'Code': PropertyMutability.MUTABLE, // No interruption
|
|
119
|
+
'Runtime': PropertyMutability.MUTABLE, // No interruption
|
|
120
|
+
'Handler': PropertyMutability.MUTABLE, // No interruption
|
|
121
|
+
'Role': PropertyMutability.MUTABLE, // No interruption
|
|
122
|
+
'Description': PropertyMutability.MUTABLE, // No interruption
|
|
123
|
+
'Timeout': PropertyMutability.MUTABLE, // No interruption
|
|
124
|
+
'MemorySize': PropertyMutability.MUTABLE, // No interruption
|
|
125
|
+
'Environment': PropertyMutability.MUTABLE, // No interruption
|
|
126
|
+
'Environment.Variables': PropertyMutability.MUTABLE, // No interruption
|
|
127
|
+
'VpcConfig': PropertyMutability.MUTABLE, // No interruption
|
|
128
|
+
'VpcConfig.SubnetIds': PropertyMutability.MUTABLE, // No interruption
|
|
129
|
+
'VpcConfig.SecurityGroupIds': PropertyMutability.MUTABLE, // No interruption
|
|
130
|
+
'VpcConfig.Ipv6AllowedForDualStack': PropertyMutability.MUTABLE, // No interruption
|
|
131
|
+
'DeadLetterConfig': PropertyMutability.MUTABLE, // No interruption
|
|
132
|
+
'TracingConfig': PropertyMutability.MUTABLE, // No interruption
|
|
133
|
+
'KMSKeyArn': PropertyMutability.MUTABLE, // No interruption
|
|
134
|
+
'Layers': PropertyMutability.MUTABLE, // No interruption
|
|
135
|
+
'ReservedConcurrentExecutions': PropertyMutability.MUTABLE, // No interruption
|
|
136
|
+
'Tags': PropertyMutability.MUTABLE, // No interruption
|
|
137
|
+
'FileSystemConfigs': PropertyMutability.MUTABLE, // No interruption
|
|
138
|
+
'Architectures': PropertyMutability.MUTABLE, // No interruption
|
|
139
|
+
'EphemeralStorage': PropertyMutability.MUTABLE, // No interruption
|
|
140
|
+
'SnapStart': PropertyMutability.MUTABLE, // No interruption
|
|
141
|
+
'RuntimeManagementConfig': PropertyMutability.MUTABLE, // No interruption
|
|
142
|
+
'LoggingConfig': PropertyMutability.MUTABLE, // No interruption
|
|
143
|
+
},
|
|
144
|
+
|
|
145
|
+
//
|
|
146
|
+
// AWS::RDS::* Resources
|
|
147
|
+
//
|
|
148
|
+
|
|
149
|
+
'AWS::RDS::DBInstance': {
|
|
150
|
+
// Immutable properties
|
|
151
|
+
'DBInstanceIdentifier': PropertyMutability.IMMUTABLE, // Replacement required
|
|
152
|
+
'Engine': PropertyMutability.IMMUTABLE, // Replacement required
|
|
153
|
+
'DBName': PropertyMutability.IMMUTABLE, // Replacement required (for some engines)
|
|
154
|
+
'AvailabilityZone': PropertyMutability.IMMUTABLE, // Replacement required
|
|
155
|
+
|
|
156
|
+
// Mutable properties
|
|
157
|
+
'AllocatedStorage': PropertyMutability.MUTABLE, // No interruption
|
|
158
|
+
'DBInstanceClass': PropertyMutability.MUTABLE, // Some interruptions
|
|
159
|
+
'EngineVersion': PropertyMutability.MUTABLE, // Some interruptions
|
|
160
|
+
'MasterUsername': PropertyMutability.MUTABLE, // No interruption (can't actually change)
|
|
161
|
+
'MasterUserPassword': PropertyMutability.MUTABLE, // No interruption
|
|
162
|
+
'BackupRetentionPeriod': PropertyMutability.MUTABLE, // No interruption
|
|
163
|
+
'DBSecurityGroups': PropertyMutability.MUTABLE, // No interruption
|
|
164
|
+
'VPCSecurityGroups': PropertyMutability.MUTABLE, // No interruption
|
|
165
|
+
'MultiAZ': PropertyMutability.MUTABLE, // No interruption
|
|
166
|
+
'PubliclyAccessible': PropertyMutability.MUTABLE, // No interruption
|
|
167
|
+
'StorageEncrypted': PropertyMutability.MUTABLE, // Some interruptions
|
|
168
|
+
'Tags': PropertyMutability.MUTABLE, // No interruption
|
|
169
|
+
},
|
|
170
|
+
|
|
171
|
+
'AWS::RDS::DBCluster': {
|
|
172
|
+
// Immutable properties
|
|
173
|
+
'DBClusterIdentifier': PropertyMutability.IMMUTABLE, // Replacement required
|
|
174
|
+
'Engine': PropertyMutability.IMMUTABLE, // Replacement required
|
|
175
|
+
'DatabaseName': PropertyMutability.IMMUTABLE, // Replacement required
|
|
176
|
+
|
|
177
|
+
// Mutable properties
|
|
178
|
+
'EngineVersion': PropertyMutability.MUTABLE, // No interruption
|
|
179
|
+
'MasterUsername': PropertyMutability.MUTABLE, // No interruption (can't actually change)
|
|
180
|
+
'MasterUserPassword': PropertyMutability.MUTABLE, // No interruption
|
|
181
|
+
'BackupRetentionPeriod': PropertyMutability.MUTABLE, // No interruption
|
|
182
|
+
'PreferredBackupWindow': PropertyMutability.MUTABLE, // No interruption
|
|
183
|
+
'PreferredMaintenanceWindow': PropertyMutability.MUTABLE, // No interruption
|
|
184
|
+
'VpcSecurityGroupIds': PropertyMutability.MUTABLE, // No interruption
|
|
185
|
+
'DBSubnetGroupName': PropertyMutability.MUTABLE, // Some interruptions
|
|
186
|
+
'StorageEncrypted': PropertyMutability.MUTABLE, // Some interruptions
|
|
187
|
+
'KmsKeyId': PropertyMutability.MUTABLE, // Some interruptions
|
|
188
|
+
'Tags': PropertyMutability.MUTABLE, // No interruption
|
|
189
|
+
},
|
|
190
|
+
|
|
191
|
+
//
|
|
192
|
+
// AWS::S3::* Resources
|
|
193
|
+
//
|
|
194
|
+
|
|
195
|
+
'AWS::S3::Bucket': {
|
|
196
|
+
// Immutable properties
|
|
197
|
+
'BucketName': PropertyMutability.IMMUTABLE, // Replacement required
|
|
198
|
+
|
|
199
|
+
// Mutable properties
|
|
200
|
+
'AccelerateConfiguration': PropertyMutability.MUTABLE, // No interruption
|
|
201
|
+
'AccessControl': PropertyMutability.MUTABLE, // No interruption
|
|
202
|
+
'AnalyticsConfigurations': PropertyMutability.MUTABLE, // No interruption
|
|
203
|
+
'BucketEncryption': PropertyMutability.MUTABLE, // No interruption
|
|
204
|
+
'CorsConfiguration': PropertyMutability.MUTABLE, // No interruption
|
|
205
|
+
'IntelligentTieringConfigurations': PropertyMutability.MUTABLE, // No interruption
|
|
206
|
+
'InventoryConfigurations': PropertyMutability.MUTABLE, // No interruption
|
|
207
|
+
'LifecycleConfiguration': PropertyMutability.MUTABLE, // No interruption
|
|
208
|
+
'LoggingConfiguration': PropertyMutability.MUTABLE, // No interruption
|
|
209
|
+
'MetricsConfigurations': PropertyMutability.MUTABLE, // No interruption
|
|
210
|
+
'NotificationConfiguration': PropertyMutability.MUTABLE, // No interruption
|
|
211
|
+
'ObjectLockConfiguration': PropertyMutability.MUTABLE, // No interruption
|
|
212
|
+
'ObjectLockEnabled': PropertyMutability.MUTABLE, // No interruption
|
|
213
|
+
'OwnershipControls': PropertyMutability.MUTABLE, // No interruption
|
|
214
|
+
'PublicAccessBlockConfiguration': PropertyMutability.MUTABLE, // No interruption
|
|
215
|
+
'ReplicationConfiguration': PropertyMutability.MUTABLE, // No interruption
|
|
216
|
+
'Tags': PropertyMutability.MUTABLE, // No interruption
|
|
217
|
+
'VersioningConfiguration': PropertyMutability.MUTABLE, // No interruption
|
|
218
|
+
'WebsiteConfiguration': PropertyMutability.MUTABLE, // No interruption
|
|
219
|
+
},
|
|
220
|
+
|
|
221
|
+
//
|
|
222
|
+
// AWS::KMS::* Resources
|
|
223
|
+
//
|
|
224
|
+
|
|
225
|
+
'AWS::KMS::Key': {
|
|
226
|
+
// All KMS key properties are mutable (updates don't require replacement)
|
|
227
|
+
'Description': PropertyMutability.MUTABLE, // No interruption
|
|
228
|
+
'Enabled': PropertyMutability.MUTABLE, // No interruption
|
|
229
|
+
'EnableKeyRotation': PropertyMutability.MUTABLE, // No interruption
|
|
230
|
+
'KeyPolicy': PropertyMutability.MUTABLE, // No interruption
|
|
231
|
+
'KeyUsage': PropertyMutability.MUTABLE, // No interruption
|
|
232
|
+
'MultiRegion': PropertyMutability.MUTABLE, // No interruption
|
|
233
|
+
'PendingWindowInDays': PropertyMutability.MUTABLE, // No interruption
|
|
234
|
+
'Tags': PropertyMutability.MUTABLE, // No interruption
|
|
235
|
+
},
|
|
236
|
+
|
|
237
|
+
//
|
|
238
|
+
// AWS::DynamoDB::* Resources
|
|
239
|
+
//
|
|
240
|
+
|
|
241
|
+
'AWS::DynamoDB::Table': {
|
|
242
|
+
// Immutable properties
|
|
243
|
+
'TableName': PropertyMutability.IMMUTABLE, // Replacement required
|
|
244
|
+
'KeySchema': PropertyMutability.IMMUTABLE, // Replacement required
|
|
245
|
+
'AttributeDefinitions': PropertyMutability.CONDITIONAL, // Some changes require replacement
|
|
246
|
+
|
|
247
|
+
// Mutable properties
|
|
248
|
+
'BillingMode': PropertyMutability.MUTABLE, // No interruption
|
|
249
|
+
'ProvisionedThroughput': PropertyMutability.MUTABLE, // No interruption
|
|
250
|
+
'GlobalSecondaryIndexes': PropertyMutability.MUTABLE, // No interruption
|
|
251
|
+
'LocalSecondaryIndexes': PropertyMutability.IMMUTABLE, // Replacement required
|
|
252
|
+
'StreamSpecification': PropertyMutability.MUTABLE, // No interruption
|
|
253
|
+
'SSESpecification': PropertyMutability.MUTABLE, // No interruption
|
|
254
|
+
'Tags': PropertyMutability.MUTABLE, // No interruption
|
|
255
|
+
'TimeToLiveSpecification': PropertyMutability.MUTABLE, // No interruption
|
|
256
|
+
'PointInTimeRecoverySpecification': PropertyMutability.MUTABLE, // No interruption
|
|
257
|
+
'ContributorInsightsSpecification': PropertyMutability.MUTABLE, // No interruption
|
|
258
|
+
'KinesisStreamSpecification': PropertyMutability.MUTABLE, // No interruption
|
|
259
|
+
},
|
|
260
|
+
|
|
261
|
+
//
|
|
262
|
+
// AWS::SQS::* Resources
|
|
263
|
+
//
|
|
264
|
+
|
|
265
|
+
'AWS::SQS::Queue': {
|
|
266
|
+
// Immutable properties
|
|
267
|
+
'QueueName': PropertyMutability.IMMUTABLE, // Replacement required
|
|
268
|
+
'FifoQueue': PropertyMutability.IMMUTABLE, // Replacement required
|
|
269
|
+
|
|
270
|
+
// Mutable properties
|
|
271
|
+
'ContentBasedDeduplication': PropertyMutability.MUTABLE, // No interruption
|
|
272
|
+
'DeduplicationScope': PropertyMutability.MUTABLE, // No interruption
|
|
273
|
+
'DelaySeconds': PropertyMutability.MUTABLE, // No interruption
|
|
274
|
+
'FifoThroughputLimit': PropertyMutability.MUTABLE, // No interruption
|
|
275
|
+
'KmsMasterKeyId': PropertyMutability.MUTABLE, // No interruption
|
|
276
|
+
'KmsDataKeyReusePeriodSeconds': PropertyMutability.MUTABLE, // No interruption
|
|
277
|
+
'MaximumMessageSize': PropertyMutability.MUTABLE, // No interruption
|
|
278
|
+
'MessageRetentionPeriod': PropertyMutability.MUTABLE, // No interruption
|
|
279
|
+
'ReceiveMessageWaitTimeSeconds': PropertyMutability.MUTABLE, // No interruption
|
|
280
|
+
'RedrivePolicy': PropertyMutability.MUTABLE, // No interruption
|
|
281
|
+
'RedriveAllowPolicy': PropertyMutability.MUTABLE, // No interruption
|
|
282
|
+
'SqsManagedSseEnabled': PropertyMutability.MUTABLE, // No interruption
|
|
283
|
+
'Tags': PropertyMutability.MUTABLE, // No interruption
|
|
284
|
+
'VisibilityTimeout': PropertyMutability.MUTABLE, // No interruption
|
|
285
|
+
},
|
|
286
|
+
|
|
287
|
+
//
|
|
288
|
+
// AWS::SNS::* Resources
|
|
289
|
+
//
|
|
290
|
+
|
|
291
|
+
'AWS::SNS::Topic': {
|
|
292
|
+
// Immutable properties
|
|
293
|
+
'TopicName': PropertyMutability.IMMUTABLE, // Replacement required
|
|
294
|
+
'FifoTopic': PropertyMutability.IMMUTABLE, // Replacement required
|
|
295
|
+
|
|
296
|
+
// Mutable properties
|
|
297
|
+
'ContentBasedDeduplication': PropertyMutability.MUTABLE, // No interruption
|
|
298
|
+
'DataProtectionPolicy': PropertyMutability.MUTABLE, // No interruption
|
|
299
|
+
'DisplayName': PropertyMutability.MUTABLE, // No interruption
|
|
300
|
+
'KmsMasterKeyId': PropertyMutability.MUTABLE, // No interruption
|
|
301
|
+
'SignatureVersion': PropertyMutability.MUTABLE, // No interruption
|
|
302
|
+
'Subscription': PropertyMutability.MUTABLE, // No interruption
|
|
303
|
+
'Tags': PropertyMutability.MUTABLE, // No interruption
|
|
304
|
+
'TracingConfig': PropertyMutability.MUTABLE, // No interruption
|
|
305
|
+
},
|
|
306
|
+
|
|
307
|
+
//
|
|
308
|
+
// AWS::IAM::* Resources
|
|
309
|
+
//
|
|
310
|
+
|
|
311
|
+
'AWS::IAM::Role': {
|
|
312
|
+
// Immutable properties
|
|
313
|
+
'RoleName': PropertyMutability.IMMUTABLE, // Replacement required
|
|
314
|
+
|
|
315
|
+
// Mutable properties
|
|
316
|
+
'AssumeRolePolicyDocument': PropertyMutability.MUTABLE, // No interruption
|
|
317
|
+
'Description': PropertyMutability.MUTABLE, // No interruption
|
|
318
|
+
'ManagedPolicyArns': PropertyMutability.MUTABLE, // No interruption
|
|
319
|
+
'MaxSessionDuration': PropertyMutability.MUTABLE, // No interruption
|
|
320
|
+
'Path': PropertyMutability.MUTABLE, // No interruption
|
|
321
|
+
'PermissionsBoundary': PropertyMutability.MUTABLE, // No interruption
|
|
322
|
+
'Policies': PropertyMutability.MUTABLE, // No interruption
|
|
323
|
+
'Tags': PropertyMutability.MUTABLE, // No interruption
|
|
324
|
+
},
|
|
325
|
+
};
|
|
326
|
+
|
|
327
|
+
/**
|
|
328
|
+
* Get property mutability for a given resource type and property path
|
|
329
|
+
*
|
|
330
|
+
* @param {string} resourceType - CloudFormation resource type (e.g., 'AWS::Lambda::Function')
|
|
331
|
+
* @param {string} propertyPath - Property path from drift detection (e.g., 'VpcConfig.SubnetIds')
|
|
332
|
+
* @returns {PropertyMutability} Property mutability (defaults to MUTABLE if not found)
|
|
333
|
+
*/
|
|
334
|
+
function getPropertyMutability(resourceType, propertyPath) {
|
|
335
|
+
// Check if resource type has configuration
|
|
336
|
+
if (!PROPERTY_MUTABILITY_CONFIG[resourceType]) {
|
|
337
|
+
// Unknown resource type - default to MUTABLE (safe, allows reconciliation attempt)
|
|
338
|
+
return PropertyMutability.MUTABLE;
|
|
339
|
+
}
|
|
340
|
+
|
|
341
|
+
const resourceConfig = PROPERTY_MUTABILITY_CONFIG[resourceType];
|
|
342
|
+
|
|
343
|
+
// Check exact property path match
|
|
344
|
+
if (resourceConfig[propertyPath]) {
|
|
345
|
+
return resourceConfig[propertyPath];
|
|
346
|
+
}
|
|
347
|
+
|
|
348
|
+
// Check parent property for nested paths (e.g., 'VpcConfig' for 'VpcConfig.SubnetIds')
|
|
349
|
+
const parentPath = propertyPath.split('.')[0];
|
|
350
|
+
if (resourceConfig[parentPath]) {
|
|
351
|
+
return resourceConfig[parentPath];
|
|
352
|
+
}
|
|
353
|
+
|
|
354
|
+
// Property not found in config - default to MUTABLE
|
|
355
|
+
return PropertyMutability.MUTABLE;
|
|
356
|
+
}
|
|
357
|
+
|
|
358
|
+
/**
|
|
359
|
+
* Check if a resource type is supported in the configuration
|
|
360
|
+
*
|
|
361
|
+
* @param {string} resourceType - CloudFormation resource type
|
|
362
|
+
* @returns {boolean} True if resource type is configured
|
|
363
|
+
*/
|
|
364
|
+
function isResourceTypeConfigured(resourceType) {
|
|
365
|
+
return resourceType in PROPERTY_MUTABILITY_CONFIG;
|
|
366
|
+
}
|
|
367
|
+
|
|
368
|
+
/**
|
|
369
|
+
* Get all configured resource types
|
|
370
|
+
*
|
|
371
|
+
* @returns {string[]} Array of resource type names
|
|
372
|
+
*/
|
|
373
|
+
function getConfiguredResourceTypes() {
|
|
374
|
+
return Object.keys(PROPERTY_MUTABILITY_CONFIG);
|
|
375
|
+
}
|
|
376
|
+
|
|
377
|
+
module.exports = {
|
|
378
|
+
PROPERTY_MUTABILITY_CONFIG,
|
|
379
|
+
getPropertyMutability,
|
|
380
|
+
isResourceTypeConfigured,
|
|
381
|
+
getConfiguredResourceTypes,
|
|
382
|
+
};
|
|
@@ -0,0 +1,245 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* TemplateParser - Parse CloudFormation templates for resource extraction
|
|
3
|
+
*
|
|
4
|
+
* Purpose: Parse both build templates (.serverless/) and deployed templates (from AWS)
|
|
5
|
+
* to extract resource definitions, logical IDs, and Refs for import mapping.
|
|
6
|
+
*/
|
|
7
|
+
|
|
8
|
+
class TemplateParser {
|
|
9
|
+
/**
|
|
10
|
+
* Parse CloudFormation template and extract resource definitions
|
|
11
|
+
* @param {string|object} template - Template path or parsed template object
|
|
12
|
+
* @returns {object} Parsed template with resources
|
|
13
|
+
*/
|
|
14
|
+
parseTemplate(template) {
|
|
15
|
+
let parsedTemplate;
|
|
16
|
+
|
|
17
|
+
if (typeof template === 'string') {
|
|
18
|
+
const fs = require('fs');
|
|
19
|
+
const path = require('path');
|
|
20
|
+
|
|
21
|
+
if (!fs.existsSync(template)) {
|
|
22
|
+
throw new Error(`Template not found at path: ${template}`);
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
parsedTemplate = JSON.parse(fs.readFileSync(template, 'utf8'));
|
|
26
|
+
} else {
|
|
27
|
+
parsedTemplate = template;
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
return {
|
|
31
|
+
resources: parsedTemplate.Resources || {},
|
|
32
|
+
version: parsedTemplate.AWSTemplateFormatVersion,
|
|
33
|
+
description: parsedTemplate.Description,
|
|
34
|
+
outputs: parsedTemplate.Outputs || {},
|
|
35
|
+
};
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
/**
|
|
39
|
+
* Extract VPC-related resource logical IDs from template
|
|
40
|
+
* @param {object} template - Parsed template object
|
|
41
|
+
* @returns {Array} VPC resources with logical IDs
|
|
42
|
+
*/
|
|
43
|
+
getVpcResources(template) {
|
|
44
|
+
const vpcResourceTypes = [
|
|
45
|
+
'AWS::EC2::VPC',
|
|
46
|
+
'AWS::EC2::Subnet',
|
|
47
|
+
'AWS::EC2::SecurityGroup',
|
|
48
|
+
'AWS::EC2::InternetGateway',
|
|
49
|
+
'AWS::EC2::NatGateway',
|
|
50
|
+
'AWS::EC2::RouteTable',
|
|
51
|
+
'AWS::EC2::VPCEndpoint',
|
|
52
|
+
];
|
|
53
|
+
|
|
54
|
+
return Object.entries(template.resources)
|
|
55
|
+
.filter(([_, resource]) => vpcResourceTypes.includes(resource.Type))
|
|
56
|
+
.map(([logicalId, resource]) => ({
|
|
57
|
+
logicalId,
|
|
58
|
+
resourceType: resource.Type,
|
|
59
|
+
properties: resource.Properties || {},
|
|
60
|
+
}));
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
/**
|
|
64
|
+
* Extract hardcoded resource IDs from deployed template
|
|
65
|
+
* Finds physical IDs that are hardcoded instead of using Refs
|
|
66
|
+
* @param {object} template - Deployed CloudFormation template
|
|
67
|
+
* @returns {object} Extracted hardcoded IDs by type
|
|
68
|
+
*/
|
|
69
|
+
extractHardcodedIds(template) {
|
|
70
|
+
const hardcodedIds = {
|
|
71
|
+
vpcIds: new Set(),
|
|
72
|
+
subnetIds: new Set(),
|
|
73
|
+
securityGroupIds: new Set(),
|
|
74
|
+
};
|
|
75
|
+
|
|
76
|
+
// Traverse template to find hardcoded IDs
|
|
77
|
+
Object.values(template.resources).forEach((resource) => {
|
|
78
|
+
this._extractIdsFromResource(resource, hardcodedIds);
|
|
79
|
+
});
|
|
80
|
+
|
|
81
|
+
return {
|
|
82
|
+
vpcIds: Array.from(hardcodedIds.vpcIds),
|
|
83
|
+
subnetIds: Array.from(hardcodedIds.subnetIds),
|
|
84
|
+
securityGroupIds: Array.from(hardcodedIds.securityGroupIds),
|
|
85
|
+
};
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
/**
|
|
89
|
+
* Extract Refs from build template
|
|
90
|
+
* Finds logical IDs that are referenced via {Ref: "LogicalId"}
|
|
91
|
+
* @param {object} template - Build template with Refs
|
|
92
|
+
* @returns {object} Logical IDs mapped to expected resource types
|
|
93
|
+
*/
|
|
94
|
+
extractRefs(template) {
|
|
95
|
+
const refs = {
|
|
96
|
+
vpcRefs: new Set(),
|
|
97
|
+
subnetRefs: new Set(),
|
|
98
|
+
securityGroupRefs: new Set(),
|
|
99
|
+
};
|
|
100
|
+
|
|
101
|
+
// Traverse template to find Ref expressions
|
|
102
|
+
Object.values(template.resources).forEach((resource) => {
|
|
103
|
+
this._extractRefsFromResource(resource, refs);
|
|
104
|
+
});
|
|
105
|
+
|
|
106
|
+
return {
|
|
107
|
+
vpcRefs: Array.from(refs.vpcRefs),
|
|
108
|
+
subnetRefs: Array.from(refs.subnetRefs),
|
|
109
|
+
securityGroupRefs: Array.from(refs.securityGroupRefs),
|
|
110
|
+
};
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
/**
|
|
114
|
+
* Recursively extract hardcoded IDs from resource properties
|
|
115
|
+
* @private
|
|
116
|
+
*/
|
|
117
|
+
_extractIdsFromResource(obj, hardcodedIds) {
|
|
118
|
+
if (typeof obj !== 'object' || obj === null) return;
|
|
119
|
+
|
|
120
|
+
Object.entries(obj).forEach(([key, value]) => {
|
|
121
|
+
// Check for VPC IDs
|
|
122
|
+
if (key === 'VpcId' && typeof value === 'string' && value.startsWith('vpc-')) {
|
|
123
|
+
hardcodedIds.vpcIds.add(value);
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
// Check for subnet IDs
|
|
127
|
+
if (
|
|
128
|
+
(key === 'SubnetIds' || key === 'SubnetId') &&
|
|
129
|
+
Array.isArray(value)
|
|
130
|
+
) {
|
|
131
|
+
value.forEach((id) => {
|
|
132
|
+
if (typeof id === 'string' && id.startsWith('subnet-')) {
|
|
133
|
+
hardcodedIds.subnetIds.add(id);
|
|
134
|
+
}
|
|
135
|
+
});
|
|
136
|
+
} else if (
|
|
137
|
+
key === 'SubnetId' &&
|
|
138
|
+
typeof value === 'string' &&
|
|
139
|
+
value.startsWith('subnet-')
|
|
140
|
+
) {
|
|
141
|
+
hardcodedIds.subnetIds.add(value);
|
|
142
|
+
}
|
|
143
|
+
|
|
144
|
+
// Check for security group IDs
|
|
145
|
+
if (key === 'SecurityGroupIds' && Array.isArray(value)) {
|
|
146
|
+
value.forEach((id) => {
|
|
147
|
+
if (typeof id === 'string' && id.startsWith('sg-')) {
|
|
148
|
+
hardcodedIds.securityGroupIds.add(id);
|
|
149
|
+
}
|
|
150
|
+
});
|
|
151
|
+
} else if (
|
|
152
|
+
key === 'GroupId' &&
|
|
153
|
+
typeof value === 'string' &&
|
|
154
|
+
value.startsWith('sg-')
|
|
155
|
+
) {
|
|
156
|
+
hardcodedIds.securityGroupIds.add(value);
|
|
157
|
+
}
|
|
158
|
+
|
|
159
|
+
// Recurse into nested objects
|
|
160
|
+
if (typeof value === 'object') {
|
|
161
|
+
this._extractIdsFromResource(value, hardcodedIds);
|
|
162
|
+
}
|
|
163
|
+
});
|
|
164
|
+
}
|
|
165
|
+
|
|
166
|
+
/**
|
|
167
|
+
* Recursively extract Refs from resource properties
|
|
168
|
+
* @private
|
|
169
|
+
*/
|
|
170
|
+
_extractRefsFromResource(obj, refs) {
|
|
171
|
+
if (typeof obj !== 'object' || obj === null) return;
|
|
172
|
+
|
|
173
|
+
Object.entries(obj).forEach(([key, value]) => {
|
|
174
|
+
// Check for Ref expressions
|
|
175
|
+
if (key === 'Ref' && typeof value === 'string') {
|
|
176
|
+
// Determine ref type based on logical ID naming
|
|
177
|
+
if (value.includes('VPC') && !value.includes('Endpoint')) {
|
|
178
|
+
refs.vpcRefs.add(value);
|
|
179
|
+
} else if (value.includes('Subnet')) {
|
|
180
|
+
refs.subnetRefs.add(value);
|
|
181
|
+
} else if (value.includes('SecurityGroup')) {
|
|
182
|
+
refs.securityGroupRefs.add(value);
|
|
183
|
+
}
|
|
184
|
+
}
|
|
185
|
+
|
|
186
|
+
// Recurse into nested objects and arrays
|
|
187
|
+
if (typeof value === 'object') {
|
|
188
|
+
this._extractRefsFromResource(value, refs);
|
|
189
|
+
}
|
|
190
|
+
});
|
|
191
|
+
}
|
|
192
|
+
|
|
193
|
+
/**
|
|
194
|
+
* Find logical ID for a physical ID by comparing templates
|
|
195
|
+
* @param {string} physicalId - Physical resource ID from AWS
|
|
196
|
+
* @param {object} deployedTemplate - Template with hardcoded IDs
|
|
197
|
+
* @param {object} buildTemplate - Template with Refs
|
|
198
|
+
* @returns {string|null} Matching logical ID or null
|
|
199
|
+
*/
|
|
200
|
+
findLogicalIdForPhysicalId(physicalId, deployedTemplate, buildTemplate) {
|
|
201
|
+
// Extract hardcoded IDs and their context
|
|
202
|
+
const hardcodedIds = this.extractHardcodedIds(deployedTemplate);
|
|
203
|
+
const refs = this.extractRefs(buildTemplate);
|
|
204
|
+
|
|
205
|
+
// Determine resource type from physical ID
|
|
206
|
+
let logicalIdCandidates = [];
|
|
207
|
+
if (physicalId.startsWith('vpc-')) {
|
|
208
|
+
logicalIdCandidates = refs.vpcRefs;
|
|
209
|
+
} else if (physicalId.startsWith('subnet-')) {
|
|
210
|
+
logicalIdCandidates = refs.subnetRefs;
|
|
211
|
+
} else if (physicalId.startsWith('sg-')) {
|
|
212
|
+
logicalIdCandidates = refs.securityGroupRefs;
|
|
213
|
+
}
|
|
214
|
+
|
|
215
|
+
// For now, return first candidate (will enhance with position matching)
|
|
216
|
+
return logicalIdCandidates[0] || null;
|
|
217
|
+
}
|
|
218
|
+
|
|
219
|
+
/**
|
|
220
|
+
* Get build template path from project directory
|
|
221
|
+
* @param {string} projectPath - Project root path
|
|
222
|
+
* @returns {string} Path to build template
|
|
223
|
+
*/
|
|
224
|
+
static getBuildTemplatePath(projectPath = process.cwd()) {
|
|
225
|
+
const path = require('path');
|
|
226
|
+
return path.join(
|
|
227
|
+
projectPath,
|
|
228
|
+
'.serverless',
|
|
229
|
+
'cloudformation-template-update-stack.json'
|
|
230
|
+
);
|
|
231
|
+
}
|
|
232
|
+
|
|
233
|
+
/**
|
|
234
|
+
* Check if build template exists
|
|
235
|
+
* @param {string} projectPath - Project root path
|
|
236
|
+
* @returns {boolean} True if template exists
|
|
237
|
+
*/
|
|
238
|
+
static buildTemplateExists(projectPath = process.cwd()) {
|
|
239
|
+
const fs = require('fs');
|
|
240
|
+
const templatePath = this.getBuildTemplatePath(projectPath);
|
|
241
|
+
return fs.existsSync(templatePath);
|
|
242
|
+
}
|
|
243
|
+
}
|
|
244
|
+
|
|
245
|
+
module.exports = { TemplateParser };
|