in packages/aws-cdk-lib/aws-rds/lib/cluster.ts [788:975]
constructor(scope: Construct, id: string, props: DatabaseClusterBaseProps) {
super(scope, id);
if (props.clusterScalabilityType !== undefined && props.clusterScailabilityType !== undefined) {
throw new ValidationError('You cannot specify both clusterScalabilityType and clusterScailabilityType (deprecated). Use clusterScalabilityType.', this);
}
if ((props.vpc && props.instanceProps?.vpc) || (!props.vpc && !props.instanceProps?.vpc)) {
throw new ValidationError('Provide either vpc or instanceProps.vpc, but not both', this);
}
if ((props.vpcSubnets && props.instanceProps?.vpcSubnets)) {
throw new ValidationError('Provide either vpcSubnets or instanceProps.vpcSubnets, but not both', this);
}
this.vpc = props.instanceProps?.vpc ?? props.vpc!;
this.vpcSubnets = props.instanceProps?.vpcSubnets ?? props.vpcSubnets;
this.cloudwatchLogGroups = {};
this.singleUserRotationApplication = props.engine.singleUserRotationApplication;
this.multiUserRotationApplication = props.engine.multiUserRotationApplication;
this.serverlessV2MaxCapacity = props.serverlessV2MaxCapacity ?? 2;
this.serverlessV2MinCapacity = props.serverlessV2MinCapacity ?? 0.5;
this.validateServerlessScalingConfig();
this.enableDataApi = props.enableDataApi;
const { subnetIds } = this.vpc.selectSubnets(this.vpcSubnets);
// Cannot test whether the subnets are in different AZs, but at least we can test the amount.
if (subnetIds.length < 2) {
Annotations.of(this).addError(`Cluster requires at least 2 subnets, got ${subnetIds.length}`);
}
this.subnetGroup = props.subnetGroup ?? new SubnetGroup(this, 'Subnets', {
description: `Subnets for ${id} database`,
vpc: this.vpc,
vpcSubnets: this.vpcSubnets,
removalPolicy: renderUnless(helperRemovalPolicy(props.removalPolicy), RemovalPolicy.DESTROY),
});
this.securityGroups = props.instanceProps?.securityGroups ?? props.securityGroups ?? [
new ec2.SecurityGroup(this, 'SecurityGroup', {
description: 'RDS security group',
vpc: this.vpc,
}),
];
const combineRoles = props.engine.combineImportAndExportRoles ?? false;
let { s3ImportRole, s3ExportRole } = setupS3ImportExport(this, props, combineRoles);
if (props.parameterGroup && props.parameters) {
throw new ValidationError('You cannot specify both parameterGroup and parameters', this);
}
const parameterGroup = props.parameterGroup ?? (
props.parameters
? new ParameterGroup(this, 'ParameterGroup', {
engine: props.engine,
parameters: props.parameters,
})
: undefined
);
// bind the engine to the Cluster
const clusterEngineBindConfig = props.engine.bindToCluster(this, {
s3ImportRole,
s3ExportRole,
parameterGroup,
});
const clusterAssociatedRoles: CfnDBCluster.DBClusterRoleProperty[] = [];
if (s3ImportRole) {
clusterAssociatedRoles.push({ roleArn: s3ImportRole.roleArn, featureName: clusterEngineBindConfig.features?.s3Import });
}
if (s3ExportRole &&
// only add the second associated Role if it's different than the first
// (duplicates in the associated Roles array are not allowed by the RDS service)
(s3ExportRole !== s3ImportRole ||
clusterEngineBindConfig.features?.s3Import !== clusterEngineBindConfig.features?.s3Export)) {
clusterAssociatedRoles.push({ roleArn: s3ExportRole.roleArn, featureName: clusterEngineBindConfig.features?.s3Export });
}
const clusterParameterGroup = props.parameterGroup ?? clusterEngineBindConfig.parameterGroup;
const clusterParameterGroupConfig = clusterParameterGroup?.bindToCluster({});
this.engine = props.engine;
const clusterIdentifier = FeatureFlags.of(this).isEnabled(cxapi.RDS_LOWERCASE_DB_IDENTIFIER) && !Token.isUnresolved(props.clusterIdentifier)
? props.clusterIdentifier?.toLowerCase()
: props.clusterIdentifier;
if (props.domain) {
this.domainId = props.domain;
this.domainRole = props.domainRole ?? new iam.Role(this, 'RDSClusterDirectoryServiceRole', {
assumedBy: new iam.CompositePrincipal(
new iam.ServicePrincipal('rds.amazonaws.com'),
new iam.ServicePrincipal('directoryservice.rds.amazonaws.com'),
),
managedPolicies: [
iam.ManagedPolicy.fromAwsManagedPolicyName('service-role/AmazonRDSDirectoryServiceAccess'),
],
});
}
validateDatabaseClusterProps(this, props);
const enablePerformanceInsights = props.enablePerformanceInsights
|| props.performanceInsightRetention !== undefined
|| props.performanceInsightEncryptionKey !== undefined
|| props.databaseInsightsMode === DatabaseInsightsMode.ADVANCED;
this.performanceInsightsEnabled = enablePerformanceInsights;
this.performanceInsightRetention = enablePerformanceInsights
? (props.performanceInsightRetention || PerformanceInsightRetention.DEFAULT)
: undefined;
this.performanceInsightEncryptionKey = props.performanceInsightEncryptionKey;
this.databaseInsightsMode = props.databaseInsightsMode;
// configure enhanced monitoring role for the cluster or instance
this.monitoringRole = props.monitoringRole;
if (!props.monitoringRole && props.monitoringInterval && props.monitoringInterval.toSeconds()) {
this.monitoringRole = new Role(this, 'MonitoringRole', {
assumedBy: new ServicePrincipal('monitoring.rds.amazonaws.com'),
managedPolicies: [
ManagedPolicy.fromAwsManagedPolicyName('service-role/AmazonRDSEnhancedMonitoringRole'),
],
});
}
if (props.enableClusterLevelEnhancedMonitoring && !props.monitoringInterval) {
throw new ValidationError('`monitoringInterval` must be set when `enableClusterLevelEnhancedMonitoring` is true.', this);
}
if (
props.monitoringInterval && !props.monitoringInterval.isUnresolved() &&
[0, 1, 5, 10, 15, 30, 60].indexOf(props.monitoringInterval.toSeconds()) === -1
) {
throw new ValidationError(`'monitoringInterval' must be one of 0, 1, 5, 10, 15, 30, or 60 seconds, got: ${props.monitoringInterval.toSeconds()} seconds.`, this);
}
this.newCfnProps = {
// Basic
engine: props.engine.engineType,
engineVersion: props.engine.engineVersion?.fullVersion,
dbClusterIdentifier: clusterIdentifier,
dbSubnetGroupName: this.subnetGroup.subnetGroupName,
vpcSecurityGroupIds: this.securityGroups.map(sg => sg.securityGroupId),
port: props.port ?? clusterEngineBindConfig.port,
dbClusterParameterGroupName: clusterParameterGroupConfig?.parameterGroupName,
associatedRoles: clusterAssociatedRoles.length > 0 ? clusterAssociatedRoles : undefined,
deletionProtection: defaultDeletionProtection(props.deletionProtection, props.removalPolicy),
enableIamDatabaseAuthentication: props.iamAuthentication,
enableHttpEndpoint: Lazy.any({ produce: () => this.enableDataApi }),
networkType: props.networkType,
serverlessV2ScalingConfiguration: Lazy.any({
produce: () => {
if (this.hasServerlessInstance) {
return {
minCapacity: this.serverlessV2MinCapacity,
maxCapacity: this.serverlessV2MaxCapacity,
};
}
return undefined;
},
}),
storageType: props.storageType?.toString(),
enableLocalWriteForwarding: props.enableLocalWriteForwarding,
clusterScalabilityType: props.clusterScalabilityType ?? props.clusterScailabilityType,
// Admin
backtrackWindow: props.backtrackWindow?.toSeconds(),
backupRetentionPeriod: props.backup?.retention?.toDays(),
preferredBackupWindow: props.backup?.preferredWindow,
preferredMaintenanceWindow: props.preferredMaintenanceWindow,
databaseName: props.defaultDatabaseName,
enableCloudwatchLogsExports: props.cloudwatchLogsExports,
// Encryption
kmsKeyId: props.storageEncryptionKey?.keyArn,
storageEncrypted: props.storageEncryptionKey ? true : props.storageEncrypted,
// Tags
copyTagsToSnapshot: props.copyTagsToSnapshot ?? true,
domain: this.domainId,
domainIamRoleName: this.domainRole?.roleName,
performanceInsightsEnabled: this.performanceInsightsEnabled || props.enablePerformanceInsights, // fall back to undefined if not set
performanceInsightsKmsKeyId: this.performanceInsightEncryptionKey?.keyArn,
performanceInsightsRetentionPeriod: this.performanceInsightRetention,
databaseInsightsMode: this.databaseInsightsMode,
autoMinorVersionUpgrade: props.autoMinorVersionUpgrade,
monitoringInterval: props.enableClusterLevelEnhancedMonitoring ? props.monitoringInterval?.toSeconds() : undefined,
monitoringRoleArn: props.enableClusterLevelEnhancedMonitoring ? this.monitoringRole?.roleArn : undefined,
engineLifecycleSupport: props.engineLifecycleSupport,
};
}