Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ export interface DependentExcluded {

export const dependentExcludedMap = getDependentExcludedMap();

function getDependentExcludedMap(): Map<string, DependentExcluded> {
function getDependentExcludedMap() {
const dependentExcludedMap = new Map<string, DependentExcluded>();

dependentExcludedMap.set('AWS::CloudFront::Distribution', {
Expand Down Expand Up @@ -66,5 +66,73 @@ function getDependentExcludedMap(): Map<string, DependentExcluded> {
HealthCheckCustomConfig: ['HealthCheckConfig'],
});

return dependentExcludedMap;
return dependentExcludedMap as ReadonlyMap<string, DependentExcluded>;
}

export type RequiredXor = string[];
export const requiredXorMap = getRequiredXorMap();

function getRequiredXorMap() {
const requiredXorMap = new Map<string, RequiredXor[]>();

requiredXorMap.set('AWS::ApplicationAutoScaling::ScalingPolicy', [['ScalingTargetId', 'ResourceId']]);

requiredXorMap.set('AWS::AutoScaling::AutoScalingGroup', [
['InstanceId', 'LaunchConfigurationName', 'LaunchTemplate', 'MixedInstancesPolicy'],
['LaunchTemplateId', 'LaunchTemplateName'], // path: /definitions/LaunchTemplateSpecification
]);

requiredXorMap.set('AWS::AutoScaling::LaunchConfiguration', [
['VirtualName', 'Ebs', 'NoDevice'], // path: /definitions/BlockDeviceMapping
]);

requiredXorMap.set('AWS::CloudFront::Distribution', [
['AcmCertificateArn', 'CloudFrontDefaultCertificate', 'IamCertificateId'], // path: /definitions/ViewerCertificate
]);

requiredXorMap.set('AWS::CloudWatch::Alarm', [['Metrics', 'MetricName']]);

requiredXorMap.set('AWS::CodePipeline::Pipeline', [['ArtifactStore', 'ArtifactStores']]);

requiredXorMap.set('AWS::EC2::Instance', [
['VirtualName', 'Ebs', 'NoDevice'], // path: /definitions/BlockDeviceMapping
]);

requiredXorMap.set('AWS::EC2::LaunchTemplate', [
['VirtualName', 'Ebs', 'NoDevice'], // path: /definitions/BlockDeviceMapping
]);

requiredXorMap.set('AWS::EC2::NetworkAclEntry', [['Ipv6CidrBlock', 'CidrBlock']]);

requiredXorMap.set('AWS::EC2::SecurityGroup', [
['CidrIp', 'CidrIpv6', 'DestinationSecurityGroupId', 'DestinationPrefixListId'], // path: /definitions/Egress
['CidrIp', 'CidrIpv6', 'SourcePrefixListId', 'SourceSecurityGroupId', 'SourceSecurityGroupName'], // path: /definitions/Ingress
]);

requiredXorMap.set('AWS::EC2::SecurityGroupEgress', [
['CidrIp', 'CidrIpv6', 'DestinationPrefixListId', 'DestinationSecurityGroupId'],
]);

requiredXorMap.set('AWS::EC2::SecurityGroupIngress', [
['CidrIp', 'CidrIpv6', 'SourcePrefixListId', 'SourceSecurityGroupId', 'SourceSecurityGroupName'],
]);

requiredXorMap.set('AWS::EC2::SpotFleet', [
['VirtualName', 'Ebs', 'NoDevice'], // path: /definitions/BlockDeviceMapping
['LaunchSpecifications', 'LaunchTemplateConfigs'], // path: /definitions/SpotFleetRequestConfigData
]);

requiredXorMap.set('AWS::EC2::VPC', [['CidrBlock', 'Ipv4IpamPoolId']]);

requiredXorMap.set('AWS::ElasticLoadBalancingV2::LoadBalancer', [['Subnets', 'SubnetMappings']]);

requiredXorMap.set('AWS::OpsWorks::Instance', [
['VirtualName', 'Ebs', 'NoDevice'], // path: /definitions/BlockDeviceMapping
]);

requiredXorMap.set('AWS::Route53::RecordSet', [['HostedZoneId', 'HostedZoneName']]);

requiredXorMap.set('AWS::Route53::RecordSetGroup', [['HostedZoneId', 'HostedZoneName']]);

return requiredXorMap as ReadonlyMap<string, RequiredXor[]>;
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
import { ResourceSchema } from '../ResourceSchema';
import { RequiredXor, requiredXorMap } from './MutuallyExclusivePropertiesForValidation';
import { ResourceTemplateTransformer } from './ResourceTemplateTransformer';

export class RemoveRequiredXorPropertiesTransformer implements ResourceTemplateTransformer {
transform(resourceProperties: Record<string, unknown>, schema: ResourceSchema): void {
const resourceType = schema.typeName;
const requiredXorData = requiredXorMap.get(resourceType);
if (!requiredXorData) {
return;
}
this.removeXorProperties(resourceProperties, requiredXorData);
}

private removeXorProperties(obj: Record<string, unknown>, requiredXorData: RequiredXor[]) {
for (const keysArray of requiredXorData) {
const firstFound = keysArray.find((key) => key in obj);

if (firstFound) {
// Remove all other keys in the xor array
for (const key of keysArray) {
if (key !== firstFound && key in obj) {
delete obj[key];
}
}
}
}

// Recursively process nested objects
for (const value of Object.values(obj)) {
if (value && typeof value === 'object' && !Array.isArray(value)) {
this.removeXorProperties(value as Record<string, unknown>, requiredXorData);
} else if (Array.isArray(value)) {
for (const item of value) {
if (item && typeof item === 'object') {
this.removeXorProperties(item as Record<string, unknown>, requiredXorData);
}
}
}
}
}
}
3 changes: 3 additions & 0 deletions src/schema/transformers/TransformersUtil.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import { ResourceStatePurpose } from '../../resourceState/ResourceStateTypes';
import { RemoveMutuallyExclusivePropertiesTransformer } from './RemoveMutuallyExclusivePropertiesTransformer';
import { RemoveReadonlyPropertiesTransformer } from './RemoveReadonlyPropertiesTransformer';
import { RemoveRequiredXorPropertiesTransformer } from './RemoveRequiredXorPropertiesTransformer';
import { RemoveSystemTagsTransformer } from './RemoveSystemTagsTransformer';
import { ReplacePrimaryIdentifierTransformer } from './ReplacePrimaryIdentifierTransformer';
import type { ResourceTemplateTransformer } from './ResourceTemplateTransformer';
Expand All @@ -12,13 +13,15 @@ export class TransformersUtil {
new RemoveReadonlyPropertiesTransformer(),
new RemoveMutuallyExclusivePropertiesTransformer(),
new RemoveSystemTagsTransformer(),
new RemoveRequiredXorPropertiesTransformer(),
];
} else if (purpose === ResourceStatePurpose.CLONE) {
return [
new RemoveReadonlyPropertiesTransformer(),
new RemoveMutuallyExclusivePropertiesTransformer(),
new RemoveSystemTagsTransformer(),
new ReplacePrimaryIdentifierTransformer(),
new RemoveRequiredXorPropertiesTransformer(),
];
}
return [];
Expand Down
Loading