in packages/aws-cdk-lib/aws-opensearchservice/lib/domain.ts [1449:2099]
constructor(scope: Construct, id: string, props: DomainProps) {
super(scope, id, {
physicalName: props.domainName,
});
// Enhanced CDK Analytics Telemetry
addConstructMetadata(this, props);
const defaultInstanceType = 'r5.large.search';
const warmDefaultInstanceType = 'ultrawarm1.medium.search';
const defaultCoordinatorInstanceType = 'm5.large.search';
const dedicatedMasterType = initializeInstanceType(defaultInstanceType, props.capacity?.masterNodeInstanceType);
const dedicatedMasterCount = props.capacity?.masterNodes ?? 0;
const dedicatedMasterEnabled = cdk.Token.isUnresolved(dedicatedMasterCount) ? true : dedicatedMasterCount > 0;
const instanceType = initializeInstanceType(defaultInstanceType, props.capacity?.dataNodeInstanceType);
const instanceCount = props.capacity?.dataNodes ?? 1;
const warmType = initializeInstanceType(warmDefaultInstanceType, props.capacity?.warmInstanceType);
const warmCount = props.capacity?.warmNodes ?? 0;
const warmEnabled = cdk.Token.isUnresolved(warmCount) ? true : warmCount > 0;
const availabilityZoneCount =
props.zoneAwareness?.availabilityZoneCount ?? 2;
if (![2, 3].includes(availabilityZoneCount)) {
throw new Error('Invalid zone awareness configuration; availabilityZoneCount must be 2 or 3');
}
const zoneAwarenessEnabled =
props.zoneAwareness?.enabled ??
props.zoneAwareness?.availabilityZoneCount != null;
let securityGroups: ec2.ISecurityGroup[] | undefined;
let subnets: ec2.ISubnet[] | undefined;
let skipZoneAwarenessCheck: boolean = false;
if (props.vpc) {
const subnetSelections = props.vpcSubnets ?? [{ subnetType: ec2.SubnetType.PRIVATE_WITH_EGRESS }];
subnets = selectSubnets(props.vpc, subnetSelections);
skipZoneAwarenessCheck = zoneAwarenessCheckShouldBeSkipped(props.vpc, subnetSelections);
securityGroups = props.securityGroups ?? [new ec2.SecurityGroup(this, 'SecurityGroup', {
vpc: props.vpc,
description: `Security group for domain ${this.node.id}`,
})];
if (props.enforceHttps) {
this._connections = new ec2.Connections({ securityGroups, defaultPort: ec2.Port.tcp(443) });
} else {
this._connections = new ec2.Connections({ securityGroups });
}
}
// If VPC options are supplied ensure that the number of subnets matches the number AZ (only if the vpc is not imported from another stack)
if (subnets &&
zoneAwarenessEnabled &&
!skipZoneAwarenessCheck &&
new Set(subnets.map((subnet) => subnet.availabilityZone)).size < availabilityZoneCount
) {
throw new Error('When providing vpc options you need to provide a subnet for each AZ you are using');
}
if ([dedicatedMasterType, instanceType, warmType].some(t => (!cdk.Token.isUnresolved(t) && !t.endsWith('.search')))) {
throw new Error('Master, data and UltraWarm node instance types must end with ".search".');
}
if (!cdk.Token.isUnresolved(warmType) && !warmType.startsWith('ultrawarm')) {
throw new Error('UltraWarm node instance type must start with "ultrawarm".');
}
const unsignedBasicAuthEnabled = props.useUnsignedBasicAuth ?? false;
if (unsignedBasicAuthEnabled) {
if (props.enforceHttps == false) {
throw new Error('You cannot disable HTTPS and use unsigned basic auth');
}
if (props.nodeToNodeEncryption == false) {
throw new Error('You cannot disable node to node encryption and use unsigned basic auth');
}
if (props.encryptionAtRest?.enabled == false) {
throw new Error('You cannot disable encryption at rest and use unsigned basic auth');
}
}
const unsignedAccessPolicy = new iam.PolicyStatement({
effect: iam.Effect.ALLOW,
actions: ['es:ESHttp*'],
principals: [new iam.AnyPrincipal()],
resources: [cdk.Lazy.string({ produce: () => `${this.domainArn}/*` })],
});
const masterUserArn = props.fineGrainedAccessControl?.masterUserArn;
const masterUserNameProps = props.fineGrainedAccessControl?.masterUserName;
// If basic auth is enabled set the user name to admin if no other user info is supplied.
const masterUserName = unsignedBasicAuthEnabled
? (masterUserArn == null ? (masterUserNameProps ?? 'admin') : undefined)
: masterUserNameProps;
if (masterUserArn != null && masterUserName != null) {
throw new Error('Invalid fine grained access control settings. Only provide one of master user ARN or master user name. Not both.');
}
const advancedSecurityEnabled = (masterUserArn ?? masterUserName) != null;
const internalUserDatabaseEnabled = masterUserName != null;
const masterUserPasswordProp = props.fineGrainedAccessControl?.masterUserPassword;
const createMasterUserPassword = (): cdk.SecretValue => {
return new secretsmanager.Secret(this, 'MasterUser', {
generateSecretString: {
secretStringTemplate: JSON.stringify({
username: masterUserName,
}),
generateStringKey: 'password',
excludeCharacters: "{}'\\*[]()`",
},
})
.secretValueFromJson('password');
};
this.masterUserPassword = internalUserDatabaseEnabled ?
(masterUserPasswordProp ?? createMasterUserPassword())
: undefined;
const encryptionAtRestEnabled =
props.encryptionAtRest?.enabled ?? (props.encryptionAtRest?.kmsKey != null || unsignedBasicAuthEnabled);
const nodeToNodeEncryptionEnabled = props.nodeToNodeEncryption ?? unsignedBasicAuthEnabled;
const volumeSize = props.ebs?.volumeSize ?? 10;
const volumeType = props.ebs?.volumeType ?? ec2.EbsDeviceVolumeType.GENERAL_PURPOSE_SSD;
const ebsEnabled = props.ebs?.enabled ?? true;
const enforceHttps = props.enforceHttps ?? unsignedBasicAuthEnabled;
function isInstanceType(t: string): Boolean {
return dedicatedMasterType.startsWith(t) || instanceType.startsWith(t);
}
function isSomeInstanceType(...instanceTypes: string[]): Boolean {
return instanceTypes.some(isInstanceType);
}
function isEveryDatanodeInstanceType(...instanceTypes: string[]): Boolean {
return instanceTypes.some(t => instanceType.startsWith(t));
}
// Validate feature support for the given Elasticsearch/OpenSearch version, per
// https://docs.aws.amazon.com/opensearch-service/latest/developerguide/features-by-version.html
const { versionNum: versionNum, isElasticsearchVersion } = parseVersion(props.version);
if (isElasticsearchVersion) {
if (
versionNum <= 7.7 &&
![
1.5, 2.3, 5.1, 5.3, 5.5, 5.6, 6.0,
6.2, 6.3, 6.4, 6.5, 6.7, 6.8, 7.1, 7.4,
7.7,
].includes(versionNum)
) {
throw new Error(`Unknown Elasticsearch version: ${versionNum}`);
}
if (versionNum < 5.1) {
if (props.logging?.appLogEnabled) {
throw new Error('Error logs publishing requires Elasticsearch version 5.1 or later or OpenSearch version 1.0 or later.');
}
if (props.encryptionAtRest?.enabled) {
throw new Error('Encryption of data at rest requires Elasticsearch version 5.1 or later or OpenSearch version 1.0 or later.');
}
if (props.cognitoDashboardsAuth != null) {
throw new Error('Cognito authentication for OpenSearch Dashboards requires Elasticsearch version 5.1 or later or OpenSearch version 1.0 or later.');
}
if (isSomeInstanceType('c5', 'i3', 'm5', 'r5')) {
throw new Error('C5, I3, M5, and R5 instance types require Elasticsearch version 5.1 or later or OpenSearch version 1.0 or later.');
}
}
if (versionNum < 6.0) {
if (props.nodeToNodeEncryption) {
throw new Error('Node-to-node encryption requires Elasticsearch version 6.0 or later or OpenSearch version 1.0 or later.');
}
}
if (versionNum < 6.7) {
if (unsignedBasicAuthEnabled) {
throw new Error('Using unsigned basic auth requires Elasticsearch version 6.7 or later or OpenSearch version 1.0 or later.');
}
if (advancedSecurityEnabled) {
throw new Error('Fine-grained access control requires Elasticsearch version 6.7 or later or OpenSearch version 1.0 or later.');
}
}
if (versionNum < 6.8 && warmEnabled) {
throw new Error('UltraWarm requires Elasticsearch version 6.8 or later or OpenSearch version 1.0 or later.');
}
}
const unSupportEbsInstanceType = [
ec2.InstanceClass.I3,
ec2.InstanceClass.R6GD,
ec2.InstanceClass.I4G,
ec2.InstanceClass.I4I,
ec2.InstanceClass.IM4GN,
ec2.InstanceClass.R7GD,
];
const supportInstanceStorageInstanceType = [
ec2.InstanceClass.R3,
...unSupportEbsInstanceType,
];
const unSupportEncryptionAtRestInstanceType=[
ec2.InstanceClass.M3,
ec2.InstanceClass.R3,
ec2.InstanceClass.T2,
];
const unSupportUltraWarmInstanceType=[
ec2.InstanceClass.T2,
ec2.InstanceClass.T3,
];
// Validate against instance type restrictions, per
// https://docs.aws.amazon.com/opensearch-service/latest/developerguide/supported-instance-types.html
if (isSomeInstanceType(...unSupportEbsInstanceType) && ebsEnabled) {
throw new Error(`${formatInstanceTypesList(unSupportEbsInstanceType, 'and')} instance types do not support EBS storage volumes.`);
}
if (isSomeInstanceType('m3', 'r3', 't2') && encryptionAtRestEnabled) {
throw new Error(`${formatInstanceTypesList(unSupportEncryptionAtRestInstanceType, 'and')} instance types do not support encryption of data at rest.`);
}
if (isInstanceType('t2.micro') && !(isElasticsearchVersion && versionNum <= 2.3)) {
throw new Error('The t2.micro.search instance type supports only Elasticsearch versions 1.5 and 2.3.');
}
if (isSomeInstanceType('t2', 't3') && warmEnabled) {
throw new Error(`${formatInstanceTypesList(unSupportUltraWarmInstanceType, 'and')} instance types do not support UltraWarm storage.`);
}
// Only R3, I3, R6GD, I4G, I4I, IM4GN and R7GD support instance storage, per
// https://aws.amazon.com/opensearch-service/pricing/
if (!ebsEnabled && !isEveryDatanodeInstanceType(...supportInstanceStorageInstanceType)) {
throw new Error(`EBS volumes are required when using instance types other than ${formatInstanceTypesList(supportInstanceStorageInstanceType, 'or')}.`);
}
// Only for a valid ebs volume configuration, per
// https://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-properties-opensearchservice-domain-ebsoptions.html
if (ebsEnabled) {
// Check if iops or throughput if general purpose is configured
if (volumeType == ec2.EbsDeviceVolumeType.GENERAL_PURPOSE_SSD || volumeType == ec2.EbsDeviceVolumeType.STANDARD) {
if (props.ebs?.iops !== undefined || props.ebs?.throughput !== undefined) {
throw new Error('General Purpose EBS volumes can not be used with Iops or Throughput configuration');
}
}
if (
volumeType &&
[
ec2.EbsDeviceVolumeType.PROVISIONED_IOPS_SSD,
].includes(volumeType) &&
!props.ebs?.iops
) {
throw new Error(
'`iops` must be specified if the `volumeType` is `PROVISIONED_IOPS_SSD`.',
);
}
if (props.ebs?.iops) {
if (
![
ec2.EbsDeviceVolumeType.PROVISIONED_IOPS_SSD,
ec2.EbsDeviceVolumeType.PROVISIONED_IOPS_SSD_IO2,
ec2.EbsDeviceVolumeType.GENERAL_PURPOSE_SSD_GP3,
].includes(volumeType)
) {
throw new Error(
'`iops` may only be specified if the `volumeType` is `PROVISIONED_IOPS_SSD`, `PROVISIONED_IOPS_SSD_IO2` or `GENERAL_PURPOSE_SSD_GP3`.',
);
}
// Enforce maximum ratio of IOPS/GiB:
// https://docs.aws.amazon.com/AWSEC2/latest/UserGuide/ebs-volume-types.html
const maximumRatios: { [key: string]: number } = {};
maximumRatios[ec2.EbsDeviceVolumeType.GENERAL_PURPOSE_SSD_GP3] = 500;
maximumRatios[ec2.EbsDeviceVolumeType.PROVISIONED_IOPS_SSD] = 50;
maximumRatios[ec2.EbsDeviceVolumeType.PROVISIONED_IOPS_SSD_IO2] = 500;
const maximumRatio = maximumRatios[volumeType];
if (props.ebs?.volumeSize && (props.ebs?.iops > maximumRatio * props.ebs?.volumeSize)) {
throw new Error(`\`${volumeType}\` volumes iops has a maximum ratio of ${maximumRatio} IOPS/GiB.`);
}
const maximumThroughputRatios: { [key: string]: number } = {};
maximumThroughputRatios[ec2.EbsDeviceVolumeType.GP3] = 0.25;
const maximumThroughputRatio = maximumThroughputRatios[volumeType];
if (props.ebs?.throughput && props.ebs?.iops) {
const iopsRatio = (props.ebs?.throughput / props.ebs?.iops);
if (iopsRatio > maximumThroughputRatio) {
throw new Error(`Throughput (MiBps) to iops ratio of ${iopsRatio} is too high; maximum is ${maximumThroughputRatio} MiBps per iops.`);
}
}
}
if (props.ebs?.throughput) {
const throughputRange = { Min: 125, Max: 1000 };
const { Min, Max } = throughputRange;
if (volumeType != ec2.EbsDeviceVolumeType.GP3) {
throw new Error(
'`throughput` property requires volumeType: `EbsDeviceVolumeType.GP3`',
);
}
if (props.ebs?.throughput < Min || props.ebs?.throughput > Max) {
throw new Error(
`throughput property takes a minimum of ${Min} and a maximum of ${Max}.`,
);
}
}
}
// Fine-grained access control requires node-to-node encryption, encryption at rest,
// and enforced HTTPS.
if (advancedSecurityEnabled) {
if (!nodeToNodeEncryptionEnabled) {
throw new Error('Node-to-node encryption is required when fine-grained access control is enabled.');
}
if (!encryptionAtRestEnabled) {
throw new Error('Encryption-at-rest is required when fine-grained access control is enabled.');
}
if (!enforceHttps) {
throw new Error('Enforce HTTPS is required when fine-grained access control is enabled.');
}
}
// Validate fine grained access control enabled for audit logs, per
// https://aws.amazon.com/about-aws/whats-new/2020/09/elasticsearch-audit-logs-now-available-on-amazon-elasticsearch-service/
if (props.logging?.auditLogEnabled && !advancedSecurityEnabled) {
throw new Error('Fine-grained access control is required when audit logs publishing is enabled.');
}
// Validate UltraWarm requirement for dedicated master nodes, per
// https://docs.aws.amazon.com/opensearch-service/latest/developerguide/ultrawarm.html
if (warmEnabled && !dedicatedMasterEnabled) {
throw new Error('Dedicated master node is required when UltraWarm storage is enabled.');
}
if (props.coldStorageEnabled && !warmEnabled) {
throw new Error('You must enable UltraWarm storage to enable cold storage.');
}
let cfnVpcOptions: CfnDomain.VPCOptionsProperty | undefined;
if (securityGroups && subnets) {
cfnVpcOptions = {
securityGroupIds: securityGroups.map((sg) => sg.securityGroupId),
subnetIds: subnets.map((subnet) => subnet.subnetId),
};
}
// Setup logging
const logGroups: logs.ILogGroup[] = [];
const logPublishing: Record<string, any> = {};
if (props.logging?.slowSearchLogEnabled) {
this.slowSearchLogGroup = props.logging.slowSearchLogGroup ??
new logs.LogGroup(this, 'SlowSearchLogs', {
retention: logs.RetentionDays.ONE_MONTH,
});
logGroups.push(this.slowSearchLogGroup);
logPublishing.SEARCH_SLOW_LOGS = {
enabled: true,
cloudWatchLogsLogGroupArn: this.slowSearchLogGroup.logGroupArn,
};
} else if (props.logging?.slowSearchLogEnabled === false) {
logPublishing.SEARCH_SLOW_LOGS = {
enabled: false,
};
}
if (props.logging?.slowIndexLogEnabled) {
this.slowIndexLogGroup = props.logging.slowIndexLogGroup ??
new logs.LogGroup(this, 'SlowIndexLogs', {
retention: logs.RetentionDays.ONE_MONTH,
});
logGroups.push(this.slowIndexLogGroup);
logPublishing.INDEX_SLOW_LOGS = {
enabled: true,
cloudWatchLogsLogGroupArn: this.slowIndexLogGroup.logGroupArn,
};
} else if (props.logging?.slowIndexLogEnabled === false) {
logPublishing.INDEX_SLOW_LOGS = {
enabled: false,
};
}
if (props.logging?.appLogEnabled) {
this.appLogGroup = props.logging.appLogGroup ??
new logs.LogGroup(this, 'AppLogs', {
retention: logs.RetentionDays.ONE_MONTH,
});
logGroups.push(this.appLogGroup);
logPublishing.ES_APPLICATION_LOGS = {
enabled: true,
cloudWatchLogsLogGroupArn: this.appLogGroup.logGroupArn,
};
} else if (props.logging?.appLogEnabled === false) {
logPublishing.ES_APPLICATION_LOGS = {
enabled: false,
};
}
if (props.logging?.auditLogEnabled) {
this.auditLogGroup = props.logging.auditLogGroup ??
new logs.LogGroup(this, 'AuditLogs', {
retention: logs.RetentionDays.ONE_MONTH,
});
logGroups.push(this.auditLogGroup);
logPublishing.AUDIT_LOGS = {
enabled: true,
cloudWatchLogsLogGroupArn: this.auditLogGroup?.logGroupArn,
};
} else if (props.logging?.auditLogEnabled === false) {
logPublishing.AUDIT_LOGS = {
enabled: false,
};
}
let logGroupResourcePolicy: LogGroupResourcePolicy | null = null;
if (logGroups.length > 0 && !props.suppressLogsResourcePolicy) {
const logPolicyStatement = new iam.PolicyStatement({
effect: iam.Effect.ALLOW,
actions: ['logs:PutLogEvents', 'logs:CreateLogStream'],
resources: logGroups.map((lg) => lg.logGroupArn),
principals: [new iam.ServicePrincipal('es.amazonaws.com')],
});
// Use a custom resource to set the log group resource policy since it is not supported by CDK and cfn.
// https://github.com/aws/aws-cdk/issues/5343
logGroupResourcePolicy = new LogGroupResourcePolicy(this, `ESLogGroupPolicy${this.node.addr}`, {
// create a cloudwatch logs resource policy name that is unique to this domain instance
policyName: `ESLogPolicy${this.node.addr}`,
policyStatements: [logPolicyStatement],
});
}
let customEndpointCertificate: acm.ICertificate | undefined;
if (props.customEndpoint) {
if (props.customEndpoint.certificate) {
customEndpointCertificate = props.customEndpoint.certificate;
} else {
customEndpointCertificate = new acm.Certificate(this, 'CustomEndpointCertificate', {
domainName: props.customEndpoint.domainName,
validation: props.customEndpoint.hostedZone ? acm.CertificateValidation.fromDns(props.customEndpoint.hostedZone) : undefined,
});
}
}
let multiAzWithStandbyEnabled = props.capacity?.multiAzWithStandbyEnabled;
if (multiAzWithStandbyEnabled === undefined) {
if (cdk.FeatureFlags.of(this).isEnabled(cxapi.ENABLE_OPENSEARCH_MULTIAZ_WITH_STANDBY)) {
multiAzWithStandbyEnabled = true;
}
}
if (isSomeInstanceType('t3') && multiAzWithStandbyEnabled) {
throw new Error('T3 instance type does not support Multi-AZ with standby feature.');
}
const offPeakWindowEnabled = props.offPeakWindowEnabled ?? props.offPeakWindowStart !== undefined;
if (offPeakWindowEnabled) {
this.validateWindowStartTime(props.offPeakWindowStart);
}
const samlAuthenticationEnabled = props.fineGrainedAccessControl?.samlAuthenticationEnabled ??
props.fineGrainedAccessControl?.samlAuthenticationOptions !== undefined;
if (samlAuthenticationEnabled) {
if (!advancedSecurityEnabled) {
throw new Error('SAML authentication requires fine-grained access control to be enabled.');
}
this.validateSamlAuthenticationOptions(props.fineGrainedAccessControl?.samlAuthenticationOptions);
}
if (props.capacity?.nodeOptions) {
// Validate coordinator node configuration
const coordinatorConfig = props.capacity.nodeOptions.find(opt => opt.nodeType === NodeType.COORDINATOR)?.nodeConfig;
if (coordinatorConfig?.enabled) {
const coordinatorType = initializeInstanceType(defaultCoordinatorInstanceType, coordinatorConfig.type);
if (!cdk.Token.isUnresolved(coordinatorType) && !coordinatorType.endsWith('.search')) {
throw new Error('Coordinator node instance type must end with ".search".');
}
if (coordinatorConfig.count !== undefined && coordinatorConfig.count < 1) {
throw new Error('Coordinator node count must be at least 1.');
}
}
}
// Create the domain
this.domain = new CfnDomain(this, 'Resource', {
domainName: this.physicalName,
engineVersion: props.version.version,
clusterConfig: {
coldStorageOptions: props.coldStorageEnabled !== undefined ? {
enabled: props.coldStorageEnabled,
} : undefined,
dedicatedMasterEnabled,
dedicatedMasterCount: dedicatedMasterEnabled
? dedicatedMasterCount
: undefined,
dedicatedMasterType: dedicatedMasterEnabled
? dedicatedMasterType
: undefined,
instanceCount,
instanceType,
multiAzWithStandbyEnabled,
warmEnabled: warmEnabled
? warmEnabled
: undefined,
warmCount: warmEnabled
? warmCount
: undefined,
warmType: warmEnabled
? warmType
: undefined,
zoneAwarenessEnabled,
zoneAwarenessConfig: zoneAwarenessEnabled
? { availabilityZoneCount }
: undefined,
nodeOptions: props.capacity?.nodeOptions,
},
ebsOptions: {
ebsEnabled,
volumeSize: ebsEnabled ? volumeSize : undefined,
volumeType: ebsEnabled ? volumeType : undefined,
iops: ebsEnabled ? props.ebs?.iops : undefined,
throughput: ebsEnabled ? props.ebs?.throughput : undefined,
},
encryptionAtRestOptions: {
enabled: encryptionAtRestEnabled,
kmsKeyId: encryptionAtRestEnabled
? props.encryptionAtRest?.kmsKey?.keyId
: undefined,
},
nodeToNodeEncryptionOptions: { enabled: nodeToNodeEncryptionEnabled },
logPublishingOptions: logPublishing,
cognitoOptions: props.cognitoDashboardsAuth ? {
enabled: true,
identityPoolId: props.cognitoDashboardsAuth?.identityPoolId,
roleArn: props.cognitoDashboardsAuth?.role.roleArn,
userPoolId: props.cognitoDashboardsAuth?.userPoolId,
} : undefined,
vpcOptions: cfnVpcOptions,
snapshotOptions: props.automatedSnapshotStartHour
? { automatedSnapshotStartHour: props.automatedSnapshotStartHour }
: undefined,
domainEndpointOptions: {
enforceHttps,
tlsSecurityPolicy: props.tlsSecurityPolicy ?? TLSSecurityPolicy.TLS_1_0,
...props.customEndpoint && {
customEndpointEnabled: true,
customEndpoint: props.customEndpoint.domainName,
customEndpointCertificateArn: customEndpointCertificate!.certificateArn,
},
},
advancedSecurityOptions: advancedSecurityEnabled
? {
enabled: true,
internalUserDatabaseEnabled,
masterUserOptions: {
masterUserArn: masterUserArn,
masterUserName: masterUserName,
masterUserPassword: this.masterUserPassword?.unsafeUnwrap(), // Safe usage
},
samlOptions: samlAuthenticationEnabled ? {
enabled: true,
idp: props.fineGrainedAccessControl && props.fineGrainedAccessControl.samlAuthenticationOptions ? {
entityId: props.fineGrainedAccessControl.samlAuthenticationOptions.idpEntityId,
metadataContent: props.fineGrainedAccessControl.samlAuthenticationOptions.idpMetadataContent,
} : undefined,
masterUserName: props.fineGrainedAccessControl?.samlAuthenticationOptions?.masterUserName,
masterBackendRole: props.fineGrainedAccessControl?.samlAuthenticationOptions?.masterBackendRole,
rolesKey: props.fineGrainedAccessControl?.samlAuthenticationOptions?.rolesKey ?? 'roles',
subjectKey: props.fineGrainedAccessControl?.samlAuthenticationOptions?.subjectKey,
sessionTimeoutMinutes: props.fineGrainedAccessControl?.samlAuthenticationOptions?.sessionTimeoutMinutes ?? 60,
} : undefined,
}
: undefined,
advancedOptions: props.advancedOptions,
offPeakWindowOptions: offPeakWindowEnabled ? {
enabled: offPeakWindowEnabled,
offPeakWindow: {
windowStartTime: props.offPeakWindowStart ?? {
hours: 22,
minutes: 0,
},
},
} : undefined,
softwareUpdateOptions: props.enableAutoSoftwareUpdate ? {
autoSoftwareUpdateEnabled: props.enableAutoSoftwareUpdate,
} : undefined,
ipAddressType: props.ipAddressType,
});
this.domain.applyRemovalPolicy(props.removalPolicy);
if (props.enableVersionUpgrade) {
this.domain.cfnOptions.updatePolicy = {
...this.domain.cfnOptions.updatePolicy,
enableVersionUpgrade: props.enableVersionUpgrade,
};
}
if (logGroupResourcePolicy) { this.domain.node.addDependency(logGroupResourcePolicy); }
if (props.domainName) {
if (!cdk.Token.isUnresolved(props.domainName)) {
// https://docs.aws.amazon.com/opensearch-service/latest/developerguide/configuration-api.html#configuration-api-datatypes-domainname
if (!props.domainName.match(/^[a-z0-9\-]+$/)) {
throw new Error(`Invalid domainName '${props.domainName}'. Valid characters are a-z (lowercase only), 0-9, and – (hyphen).`);
}
if (props.domainName.length < 3 || props.domainName.length > 28) {
throw new Error(`Invalid domainName '${props.domainName}'. It must be between 3 and 28 characters`);
}
if (props.domainName[0] < 'a' || props.domainName[0] > 'z') {
throw new Error(`Invalid domainName '${props.domainName}'. It must start with a lowercase letter`);
}
}
this.node.addMetadata('aws:cdk:hasPhysicalName', props.domainName);
}
this.domainName = this.getResourceNameAttribute(this.domain.ref);
this.domainId = this.domain.getAtt('Id').toString();
this.domainEndpoint = this.domain.getAtt('DomainEndpoint').toString();
this.domainArn = this.getResourceArnAttribute(this.domain.attrArn, {
service: 'es',
resource: 'domain',
resourceName: this.physicalName,
});
if (props.customEndpoint?.hostedZone) {
new route53.CnameRecord(this, 'CnameRecord', {
recordName: props.customEndpoint.domainName,
zone: props.customEndpoint.hostedZone,
domainName: this.domainEndpoint,
});
}
this.encryptionAtRestOptions = props.encryptionAtRest;
if (props.accessPolicies) {
this.addAccessPolicies(...props.accessPolicies);
}
if (unsignedBasicAuthEnabled) {
this.addAccessPolicies(unsignedAccessPolicy);
}
}