UNPKG

@aws-cdk/aws-msk-alpha

Version:

The CDK Construct Library for AWS::MSK

700 lines 96.3 kB
"use strict"; var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) { if (k2 === undefined) k2 = k; var desc = Object.getOwnPropertyDescriptor(m, k); if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) { desc = { enumerable: true, get: function() { return m[k]; } }; } Object.defineProperty(o, k2, desc); }) : (function(o, m, k, k2) { if (k2 === undefined) k2 = k; o[k2] = m[k]; })); var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) { Object.defineProperty(o, "default", { enumerable: true, value: v }); }) : function(o, v) { o["default"] = v; }); var __runInitializers = (this && this.__runInitializers) || function (thisArg, initializers, value) { var useValue = arguments.length > 2; for (var i = 0; i < initializers.length; i++) { value = useValue ? initializers[i].call(thisArg, value) : initializers[i].call(thisArg); } return useValue ? value : void 0; }; var __esDecorate = (this && this.__esDecorate) || function (ctor, descriptorIn, decorators, contextIn, initializers, extraInitializers) { function accept(f) { if (f !== void 0 && typeof f !== "function") throw new TypeError("Function expected"); return f; } var kind = contextIn.kind, key = kind === "getter" ? "get" : kind === "setter" ? "set" : "value"; var target = !descriptorIn && ctor ? contextIn["static"] ? ctor : ctor.prototype : null; var descriptor = descriptorIn || (target ? Object.getOwnPropertyDescriptor(target, contextIn.name) : {}); var _, done = false; for (var i = decorators.length - 1; i >= 0; i--) { var context = {}; for (var p in contextIn) context[p] = p === "access" ? {} : contextIn[p]; for (var p in contextIn.access) context.access[p] = contextIn.access[p]; context.addInitializer = function (f) { if (done) throw new TypeError("Cannot add initializers after decoration has completed"); extraInitializers.push(accept(f || null)); }; var result = (0, decorators[i])(kind === "accessor" ? { get: descriptor.get, set: descriptor.set } : descriptor[key], context); if (kind === "accessor") { if (result === void 0) continue; if (result === null || typeof result !== "object") throw new TypeError("Object expected"); if (_ = accept(result.get)) descriptor.get = _; if (_ = accept(result.set)) descriptor.set = _; if (_ = accept(result.init)) initializers.unshift(_); } else if (_ = accept(result)) { if (kind === "field") initializers.unshift(_); else descriptor[key] = _; } } if (target) Object.defineProperty(target, contextIn.name, descriptor); done = true; }; var __importStar = (this && this.__importStar) || (function () { var ownKeys = function(o) { ownKeys = Object.getOwnPropertyNames || function (o) { var ar = []; for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k; return ar; }; return ownKeys(o); }; return function (mod) { if (mod && mod.__esModule) return mod; var result = {}; if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]); __setModuleDefault(result, mod); return result; }; })(); Object.defineProperty(exports, "__esModule", { value: true }); exports.Cluster = exports.ClientAuthentication = exports.ClientBrokerEncryption = exports.ClusterMonitoringLevel = exports.BrokerType = exports.StorageMode = exports.ClusterBase = void 0; const jsiiDeprecationWarnings = require("../.warnings.jsii.js"); const JSII_RTTI_SYMBOL_1 = Symbol.for("jsii.rtti"); const ec2 = __importStar(require("aws-cdk-lib/aws-ec2")); const iam = __importStar(require("aws-cdk-lib/aws-iam")); const kms = __importStar(require("aws-cdk-lib/aws-kms")); const aws_msk_1 = require("aws-cdk-lib/aws-msk"); const secretsmanager = __importStar(require("aws-cdk-lib/aws-secretsmanager")); const core = __importStar(require("aws-cdk-lib/core")); const core_1 = require("aws-cdk-lib/core"); const helpers_internal_1 = require("aws-cdk-lib/core/lib/helpers-internal"); const metadata_resource_1 = require("aws-cdk-lib/core/lib/metadata-resource"); const prop_injectable_1 = require("aws-cdk-lib/core/lib/prop-injectable"); const cr = __importStar(require("aws-cdk-lib/custom-resources")); const cx_api_1 = require("aws-cdk-lib/cx-api"); const uniqueid_1 = require("constructs/lib/private/uniqueid"); /** * A new or imported MSK Cluster. */ class ClusterBase extends core.Resource { static [JSII_RTTI_SYMBOL_1] = { fqn: "@aws-cdk/aws-msk-alpha.ClusterBase", version: "2.260.0-alpha.0" }; /** @internal */ _connections; /** Manages connections for the cluster */ get connections() { if (!this._connections) { throw new core.ValidationError((0, helpers_internal_1.lit) `ImportedClusterSecurityGroups`, 'An imported Cluster cannot manage its security groups', this); } return this._connections; } } exports.ClusterBase = ClusterBase; /** * The storage mode for the cluster brokers. */ var StorageMode; (function (StorageMode) { /** * Local storage mode utilizes network attached EBS storage. */ StorageMode["LOCAL"] = "LOCAL"; /** * Tiered storage mode utilizes EBS storage and Tiered storage. */ StorageMode["TIERED"] = "TIERED"; })(StorageMode || (exports.StorageMode = StorageMode = {})); /** * The broker type for the cluster. */ var BrokerType; (function (BrokerType) { /** * Standard brokers provide high-availability guarantees. */ BrokerType["STANDARD"] = "STANDARD"; /** * Express brokers are a low-cost option for development, testing, and workloads that don't require the high availability guarantees of standard MSK cluster. */ BrokerType["EXPRESS"] = "EXPRESS"; })(BrokerType || (exports.BrokerType = BrokerType = {})); /** * The level of monitoring for the MSK cluster * * @see https://docs.aws.amazon.com/msk/latest/developerguide/monitoring.html#metrics-details */ var ClusterMonitoringLevel; (function (ClusterMonitoringLevel) { /** * Default metrics are the essential metrics to monitor. */ ClusterMonitoringLevel["DEFAULT"] = "DEFAULT"; /** * Per Broker metrics give you metrics at the broker level. */ ClusterMonitoringLevel["PER_BROKER"] = "PER_BROKER"; /** * Per Topic Per Broker metrics help you understand volume at the topic level. */ ClusterMonitoringLevel["PER_TOPIC_PER_BROKER"] = "PER_TOPIC_PER_BROKER"; /** * Per Topic Per Partition metrics help you understand consumer group lag at the topic partition level. */ ClusterMonitoringLevel["PER_TOPIC_PER_PARTITION"] = "PER_TOPIC_PER_PARTITION"; })(ClusterMonitoringLevel || (exports.ClusterMonitoringLevel = ClusterMonitoringLevel = {})); /** * Indicates the encryption setting for data in transit between clients and brokers. */ var ClientBrokerEncryption; (function (ClientBrokerEncryption) { /** * TLS means that client-broker communication is enabled with TLS only. */ ClientBrokerEncryption["TLS"] = "TLS"; /** * TLS_PLAINTEXT means that client-broker communication is enabled for both TLS-encrypted, as well as plaintext data. */ ClientBrokerEncryption["TLS_PLAINTEXT"] = "TLS_PLAINTEXT"; /** * PLAINTEXT means that client-broker communication is enabled in plaintext only. */ ClientBrokerEncryption["PLAINTEXT"] = "PLAINTEXT"; })(ClientBrokerEncryption || (exports.ClientBrokerEncryption = ClientBrokerEncryption = {})); /** * Configuration properties for client authentication. */ class ClientAuthentication { saslProps; tlsProps; static [JSII_RTTI_SYMBOL_1] = { fqn: "@aws-cdk/aws-msk-alpha.ClientAuthentication", version: "2.260.0-alpha.0" }; /** * SASL authentication */ static sasl(props) { try { jsiiDeprecationWarnings._aws_cdk_aws_msk_alpha_SaslAuthProps(props); } catch (error) { if (process.env.JSII_DEBUG !== "1" && error.name === "DeprecationError") { Error.captureStackTrace(error, this.sasl); } throw error; } return new ClientAuthentication(props, undefined); } /** * TLS authentication */ static tls(props) { try { jsiiDeprecationWarnings._aws_cdk_aws_msk_alpha_TlsAuthProps(props); } catch (error) { if (process.env.JSII_DEBUG !== "1" && error.name === "DeprecationError") { Error.captureStackTrace(error, this.tls); } throw error; } return new ClientAuthentication(undefined, props); } /** * SASL + TLS authentication */ static saslTls(saslTlsProps) { try { jsiiDeprecationWarnings._aws_cdk_aws_msk_alpha_SaslTlsAuthProps(saslTlsProps); } catch (error) { if (process.env.JSII_DEBUG !== "1" && error.name === "DeprecationError") { Error.captureStackTrace(error, this.saslTls); } throw error; } return new ClientAuthentication(saslTlsProps, saslTlsProps); } /** * @param saslProps - properties for SASL authentication * @param tlsProps - properties for TLS authentication */ constructor(saslProps, tlsProps) { this.saslProps = saslProps; this.tlsProps = tlsProps; } } exports.ClientAuthentication = ClientAuthentication; /** * Create a MSK Cluster. * * @resource AWS::MSK::Cluster */ let Cluster = (() => { let _classDecorators = [prop_injectable_1.propertyInjectable]; let _classDescriptor; let _classExtraInitializers = []; let _classThis; let _classSuper = ClusterBase; let _instanceExtraInitializers = []; let _get_clusterName_decorators; let _get_clusterArn_decorators; let _addUser_decorators; var Cluster = class extends _classSuper { static { _classThis = this; } static { const _metadata = typeof Symbol === "function" && Symbol.metadata ? Object.create(_classSuper[Symbol.metadata] ?? null) : void 0; _get_clusterName_decorators = [helpers_internal_1.memoizedGetter]; _get_clusterArn_decorators = [helpers_internal_1.memoizedGetter]; _addUser_decorators = [(0, metadata_resource_1.MethodMetadata)()]; __esDecorate(this, null, _get_clusterName_decorators, { kind: "getter", name: "clusterName", static: false, private: false, access: { has: obj => "clusterName" in obj, get: obj => obj.clusterName }, metadata: _metadata }, null, _instanceExtraInitializers); __esDecorate(this, null, _get_clusterArn_decorators, { kind: "getter", name: "clusterArn", static: false, private: false, access: { has: obj => "clusterArn" in obj, get: obj => obj.clusterArn }, metadata: _metadata }, null, _instanceExtraInitializers); __esDecorate(this, null, _addUser_decorators, { kind: "method", name: "addUser", static: false, private: false, access: { has: obj => "addUser" in obj, get: obj => obj.addUser }, metadata: _metadata }, null, _instanceExtraInitializers); __esDecorate(null, _classDescriptor = { value: _classThis }, _classDecorators, { kind: "class", name: _classThis.name, metadata: _metadata }, null, _classExtraInitializers); Cluster = _classThis = _classDescriptor.value; if (_metadata) Object.defineProperty(_classThis, Symbol.metadata, { enumerable: true, configurable: true, writable: true, value: _metadata }); } static [JSII_RTTI_SYMBOL_1] = { fqn: "@aws-cdk/aws-msk-alpha.Cluster", version: "2.260.0-alpha.0" }; /** Uniquely identifies this class. */ static PROPERTY_INJECTION_ID = '@aws-cdk.aws-msk-alpha.Cluster'; /** * Reference an existing cluster, defined outside of the CDK code, by name. */ static fromClusterArn(scope, id, clusterArn) { class Import extends ClusterBase { clusterArn = clusterArn; clusterName = core.Fn.select(1, core.Fn.split('/', clusterArn)); // ['arn:partition:kafka:region:account-id', clusterName, clusterId] } return new Import(scope, id); } /** Key used to encrypt SASL/SCRAM users */ saslScramAuthenticationKey = __runInitializers(this, _instanceExtraInitializers); resource; _clusterDescription; _clusterBootstrapBrokers; constructor(scope, id, props) { super(scope, id, { physicalName: props.clusterName }); try { jsiiDeprecationWarnings._aws_cdk_aws_msk_alpha_ClusterProps(props); } catch (error) { if (process.env.JSII_DEBUG !== "1" && error.name === "DeprecationError") { Error.captureStackTrace(error, Cluster); } throw error; } // Enhanced CDK Analytics Telemetry (0, metadata_resource_1.addConstructMetadata)(this, props); const subnetSelection = props.vpc.selectSubnets(props.vpcSubnets); this._connections = new ec2.Connections({ securityGroups: props.securityGroups ?? [ new ec2.SecurityGroup(this, 'SecurityGroup', { description: 'MSK security group', vpc: props.vpc, }), ], }); if (subnetSelection.subnets.length < 2) { throw new core.ValidationError((0, helpers_internal_1.lit) `InsufficientSubnets`, `Cluster requires at least 2 subnets, got ${subnetSelection.subnets.length}`, this); } if (props.encryptionInTransit?.clientBroker === ClientBrokerEncryption.PLAINTEXT && props.clientAuthentication) { throw new core.ValidationError((0, helpers_internal_1.lit) `ClientAuthRequiresTls`, 'To enable client authentication, you must enabled TLS-encrypted traffic between clients and brokers.', this); } else if (props.encryptionInTransit?.clientBroker === ClientBrokerEncryption.TLS_PLAINTEXT && (props.clientAuthentication?.saslProps?.scram || props.clientAuthentication?.saslProps?.iam)) { throw new core.ValidationError((0, helpers_internal_1.lit) `SaslAuthRequiresTlsOnly`, 'To enable SASL/SCRAM or IAM authentication, you must only allow TLS-encrypted traffic between clients and brokers.', this); } const volumeSize = props.ebsStorageInfo?.volumeSize ?? 1000; // Minimum: 1 GiB, maximum: 16384 GiB if (volumeSize < 1 || volumeSize > 16384) { throw new core.ValidationError((0, helpers_internal_1.lit) `InvalidEbsVolumeSize`, 'EBS volume size should be in the range 1-16384', this); } const isExpress = props.brokerType === BrokerType.EXPRESS; if (isExpress) { // Validate Kafka version compatibility // express broker documentation for supported versions https://docs.aws.amazon.com/msk/latest/developerguide/msk-broker-types-express.html#msk-broker-types-express-notes const supportedVersions = ['3.6', '3.8', '3.9']; const kafkaVersionString = props.kafkaVersion.version; const isCompatibleVersion = supportedVersions.some(version => kafkaVersionString.includes(version)); if (!isCompatibleVersion) { throw new core.ValidationError((0, helpers_internal_1.lit) `ExpressBrokerIncompatibleVersion`, `Express brokers are only supported with Apache Kafka ${supportedVersions.join(', ')}, got ${kafkaVersionString}`, this); } if (!props.instanceType) { throw new core.ValidationError((0, helpers_internal_1.lit) `ExpressBrokerRequiresInstanceType`, '`instanceType` must also be specified when `brokerType` is `BrokerType.EXPRESS`.', this); } if (props.ebsStorageInfo) { throw new core.ValidationError((0, helpers_internal_1.lit) `ExpressBrokerNoEbsStorage`, '`ebsStorageInfo` is not supported when `brokerType` is `BrokerType.EXPRESS`.', this); } if (props.storageMode) { throw new core.ValidationError((0, helpers_internal_1.lit) `ExpressBrokerNoStorageMode`, '`storageMode` is not supported when `brokerType` is `BrokerType.EXPRESS`.', this); } if (props.logging) { throw new core.ValidationError((0, helpers_internal_1.lit) `ExpressBrokerNoLogging`, '`logging` is not supported when `brokerType` is `BrokerType.EXPRESS`.', this); } if (subnetSelection.subnets.length < 3) { throw new core.ValidationError((0, helpers_internal_1.lit) `ExpressClusterInsufficientSubnets`, `Express cluster requires at least 3 subnets, got ${subnetSelection.subnets.length}`, this); } } const instanceType = props.instanceType ? this.mskInstanceType(props.instanceType, isExpress) : this.mskInstanceType(ec2.InstanceType.of(ec2.InstanceClass.M5, ec2.InstanceSize.LARGE)); if (props.storageMode && props.storageMode === StorageMode.TIERED) { if (!props.kafkaVersion.isTieredStorageCompatible()) { throw new core.ValidationError((0, helpers_internal_1.lit) `TieredStorageIncompatibleVersion`, `To deploy a tiered cluster you must select a compatible Kafka version, got ${props.kafkaVersion.version}`, this); } if (instanceType === this.mskInstanceType(ec2.InstanceType.of(ec2.InstanceClass.T3, ec2.InstanceSize.SMALL))) { throw new core.ValidationError((0, helpers_internal_1.lit) `TieredStorageIncompatibleInstanceType`, 'Tiered storage doesn\'t support broker type t3.small', this); } } const encryptionAtRest = props.ebsStorageInfo?.encryptionKey ? { dataVolumeKmsKeyId: props.ebsStorageInfo.encryptionKey.keyRef.keyId, } : undefined; // MSK will create the managed key const encryptionInTransit = { clientBroker: props.encryptionInTransit?.clientBroker ?? ClientBrokerEncryption.TLS, inCluster: props.encryptionInTransit?.enableInCluster ?? true, }; const openMonitoring = props.monitoring?.enablePrometheusJmxExporter || props.monitoring?.enablePrometheusNodeExporter ? { prometheus: { jmxExporter: props.monitoring?.enablePrometheusJmxExporter ? { enabledInBroker: true } : undefined, nodeExporter: props.monitoring ?.enablePrometheusNodeExporter ? { enabledInBroker: true } : undefined, }, } : undefined; const loggingBucket = props.logging?.s3?.bucket; if (loggingBucket && core_1.FeatureFlags.of(this).isEnabled(cx_api_1.S3_CREATE_DEFAULT_LOGGING_POLICY)) { const stack = core.Stack.of(this); loggingBucket.addToResourcePolicy(new iam.PolicyStatement({ effect: iam.Effect.ALLOW, principals: [ new iam.ServicePrincipal('delivery.logs.amazonaws.com'), ], resources: [ loggingBucket.arnForObjects(`AWSLogs/${stack.account}/*`), ], actions: ['s3:PutObject'], conditions: { StringEquals: { 's3:x-amz-acl': 'bucket-owner-full-control', 'aws:SourceAccount': stack.account, }, ArnLike: { 'aws:SourceArn': stack.formatArn({ service: 'logs', resource: '*', }), }, }, })); loggingBucket.addToResourcePolicy(new iam.PolicyStatement({ effect: iam.Effect.ALLOW, principals: [ new iam.ServicePrincipal('delivery.logs.amazonaws.com'), ], resources: [loggingBucket.bucketArn], actions: [ 's3:GetBucketAcl', 's3:ListBucket', ], conditions: { StringEquals: { 'aws:SourceAccount': stack.account, }, ArnLike: { 'aws:SourceArn': stack.formatArn({ service: 'logs', resource: '*', }), }, }, })); } const loggingInfo = isExpress ? undefined : { brokerLogs: { cloudWatchLogs: { enabled: props.logging?.cloudwatchLogGroup !== undefined, logGroup: props.logging?.cloudwatchLogGroup?.logGroupName, }, firehose: { enabled: props.logging?.firehoseDeliveryStreamName !== undefined, deliveryStream: props.logging?.firehoseDeliveryStreamName, }, s3: { enabled: loggingBucket !== undefined, bucket: loggingBucket?.bucketName, prefix: props.logging?.s3?.prefix, }, }, }; if (props.clientAuthentication?.saslProps?.scram && props.clientAuthentication?.saslProps?.key === undefined) { this.saslScramAuthenticationKey = new kms.Key(this, 'SASLKey', { description: 'Used for encrypting MSK secrets for SASL/SCRAM authentication.', alias: `msk/${props.clusterName}/sasl/scram`, }); // https://docs.aws.amazon.com/kms/latest/developerguide/services-secrets-manager.html#asm-policies this.saslScramAuthenticationKey.addToResourcePolicy(new iam.PolicyStatement({ sid: 'Allow access through AWS Secrets Manager for all principals in the account that are authorized to use AWS Secrets Manager', principals: [new iam.AnyPrincipal()], actions: [ 'kms:Encrypt', 'kms:Decrypt', 'kms:ReEncrypt*', 'kms:GenerateDataKey*', 'kms:CreateGrant', 'kms:DescribeKey', ], resources: ['*'], conditions: { StringEquals: { 'kms:ViaService': `secretsmanager.${core.Stack.of(this).region}.amazonaws.com`, 'kms:CallerAccount': core.Stack.of(this).account, }, }, })); } let clientAuthentication; if (props.clientAuthentication) { const { saslProps, tlsProps } = props.clientAuthentication; clientAuthentication = { sasl: saslProps ? { iam: saslProps.iam ? { enabled: true } : undefined, scram: saslProps.scram ? { enabled: true } : undefined, } : undefined, tls: tlsProps?.certificateAuthorities ? { certificateAuthorityArnList: tlsProps.certificateAuthorities?.map((ca) => ca.certificateAuthorityArn), } : undefined, }; } const resource = new aws_msk_1.CfnCluster(this, 'Resource', { clusterName: props.clusterName, kafkaVersion: props.kafkaVersion.version, numberOfBrokerNodes: props.numberOfBrokerNodes !== undefined ? subnetSelection.availabilityZones.length * props.numberOfBrokerNodes : subnetSelection.availabilityZones.length, brokerNodeGroupInfo: { instanceType, clientSubnets: subnetSelection.subnetIds, securityGroups: this.connections.securityGroups.map((group) => group.securityGroupId), storageInfo: isExpress ? undefined : { ebsStorageInfo: { volumeSize: volumeSize, }, }, }, encryptionInfo: { encryptionAtRest, encryptionInTransit, }, configurationInfo: props.configurationInfo, enhancedMonitoring: props.monitoring?.clusterMonitoringLevel, openMonitoring: openMonitoring, storageMode: isExpress ? undefined : props.storageMode, loggingInfo: loggingInfo, clientAuthentication, }); this.resource = resource; resource.applyRemovalPolicy(props.removalPolicy, { default: core.RemovalPolicy.RETAIN, }); } get clusterName() { return this.getResourceNameAttribute(core.Fn.select(1, core.Fn.split('/', this.resource.ref))); } get clusterArn() { return this.resource.ref; } mskInstanceType(instanceType, express) { const prefix = express ? 'express.' : 'kafka.'; return `${prefix}${instanceType.toString()}`; } /** * Get the ZooKeeper Connection string * * Uses a Custom Resource to make an API call to `describeCluster` using the Javascript SDK * * @param responseField Field to return from API call. eg. ZookeeperConnectString, ZookeeperConnectStringTls * @returns - The connection string to use to connect to the Apache ZooKeeper cluster. */ _zookeeperConnectionString(responseField) { if (!this._clusterDescription) { this._clusterDescription = new cr.AwsCustomResource(this, 'ZookeeperConnect', { onUpdate: { service: 'Kafka', action: 'describeCluster', parameters: { ClusterArn: this.clusterArn, }, physicalResourceId: cr.PhysicalResourceId.of('ZooKeeperConnectionString'), // Limit the output of describeCluster that is otherwise too large outputPaths: [ 'ClusterInfo.ZookeeperConnectString', 'ClusterInfo.ZookeeperConnectStringTls', ], }, policy: cr.AwsCustomResourcePolicy.fromSdkCalls({ resources: [this.clusterArn], }), installLatestAwsSdk: false, }); } return this._clusterDescription.getResponseField(`ClusterInfo.${responseField}`); } /** * Get the ZooKeeper Connection string * * Uses a Custom Resource to make an API call to `describeCluster` using the Javascript SDK * * @returns - The connection string to use to connect to the Apache ZooKeeper cluster. */ get zookeeperConnectionString() { return this._zookeeperConnectionString('ZookeeperConnectString'); } /** * Get the ZooKeeper Connection string for a TLS enabled cluster * * Uses a Custom Resource to make an API call to `describeCluster` using the Javascript SDK * * @returns - The connection string to use to connect to zookeeper cluster on TLS port. */ get zookeeperConnectionStringTls() { return this._zookeeperConnectionString('ZookeeperConnectStringTls'); } /** * Get the list of brokers that a client application can use to bootstrap * * Uses a Custom Resource to make an API call to `getBootstrapBrokers` using the Javascript SDK * * @param responseField Field to return from API call. eg. BootstrapBrokerStringSaslScram, BootstrapBrokerString * @returns - A string containing one or more hostname:port pairs. */ _bootstrapBrokers(responseField) { if (!this._clusterBootstrapBrokers) { this._clusterBootstrapBrokers = new cr.AwsCustomResource(this, `BootstrapBrokers${responseField}`, { onUpdate: { service: 'Kafka', action: 'getBootstrapBrokers', parameters: { ClusterArn: this.clusterArn, }, physicalResourceId: cr.PhysicalResourceId.of('BootstrapBrokers'), }, policy: cr.AwsCustomResourcePolicy.fromSdkCalls({ resources: [this.clusterArn], }), // APIs are available in 2.1055.0 installLatestAwsSdk: false, }); } return this._clusterBootstrapBrokers.getResponseField(responseField); } /** * Get the list of brokers that a client application can use to bootstrap * * Uses a Custom Resource to make an API call to `getBootstrapBrokers` using the Javascript SDK * * @returns - A string containing one or more hostname:port pairs. */ get bootstrapBrokers() { return this._bootstrapBrokers('BootstrapBrokerString'); } /** * Get the list of brokers that a TLS authenticated client application can use to bootstrap * * Uses a Custom Resource to make an API call to `getBootstrapBrokers` using the Javascript SDK * * @returns - A string containing one or more DNS names (or IP) and TLS port pairs. */ get bootstrapBrokersTls() { return this._bootstrapBrokers('BootstrapBrokerStringTls'); } /** * Get the list of brokers that a SASL/SCRAM authenticated client application can use to bootstrap * * Uses a Custom Resource to make an API call to `getBootstrapBrokers` using the Javascript SDK * * @returns - A string containing one or more dns name (or IP) and SASL SCRAM port pairs. */ get bootstrapBrokersSaslScram() { return this._bootstrapBrokers('BootstrapBrokerStringSaslScram'); } /** * Get the list of brokers that a SASL/IAM authenticated client application can use to bootstrap * * Uses a Custom Resource to make an API call to `getBootstrapBrokers` using the Javascript SDK * * @returns - A string containing one or more DNS names (or IP) and TLS port pairs. */ get bootstrapBrokersSaslIam() { return this._bootstrapBrokers('BootstrapBrokerStringSaslIam'); } /** * A list of usersnames to register with the cluster. The password will automatically be generated using Secrets * Manager and the { username, password } JSON object stored in Secrets Manager as `AmazonMSK_username`. * * Must be using the SASL/SCRAM authentication mechanism. * * @param usernames - username(s) to register with the cluster */ addUser(...usernames) { if (this.saslScramAuthenticationKey) { const MSK_SECRET_PREFIX = 'AmazonMSK_'; // Required const secrets = usernames.map((username) => new secretsmanager.Secret(this, `KafkaUser${username}`, { secretName: `${MSK_SECRET_PREFIX}${this.clusterName}_${username}`, generateSecretString: { secretStringTemplate: JSON.stringify({ username }), generateStringKey: 'password', }, encryptionKey: this.saslScramAuthenticationKey, })); new cr.AwsCustomResource(this, `BatchAssociateScramSecrets${(0, uniqueid_1.addressOf)(usernames)}`, { onUpdate: { service: 'Kafka', action: 'batchAssociateScramSecret', parameters: { ClusterArn: this.clusterArn, SecretArnList: secrets.map((secret) => secret.secretArn), }, physicalResourceId: cr.PhysicalResourceId.of('CreateUsers'), }, policy: cr.AwsCustomResourcePolicy.fromStatements([ new iam.PolicyStatement({ actions: ['kms:CreateGrant'], resources: [this.saslScramAuthenticationKey?.keyArn], }), new iam.PolicyStatement({ actions: ['kafka:BatchAssociateScramSecret'], resources: [this.clusterArn], }), ]), installLatestAwsSdk: false, }); } else { throw new core.ValidationError((0, helpers_internal_1.lit) `MissingAuthenticationKmsKey`, 'Cannot create users if an authentication KMS key has not been created/provided.', this); } } static { __runInitializers(_classThis, _classExtraInitializers); } }; return Cluster = _classThis; })(); exports.Cluster = Cluster; //# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiY2x1c3Rlci5qcyIsInNvdXJjZVJvb3QiOiIiLCJzb3VyY2VzIjpbImNsdXN0ZXIudHMiXSwibmFtZXMiOltdLCJtYXBwaW5ncyI6Ijs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7O0FBQ0EseURBQTJDO0FBQzNDLHlEQUEyQztBQUMzQyx5REFBMkM7QUFFM0MsaURBQWlEO0FBRWpELCtFQUFpRTtBQUNqRSx1REFBeUM7QUFDekMsMkNBQWdEO0FBQ2hELDRFQUE0RTtBQUM1RSw4RUFBOEY7QUFDOUYsMEVBQTBFO0FBQzFFLGlFQUFtRDtBQUNuRCwrQ0FBc0U7QUFFdEUsOERBQTREO0FBc0I1RDs7R0FFRztBQUNILE1BQXNCLFdBQVksU0FBUSxJQUFJLENBQUMsUUFBUTs7SUFHckQsZ0JBQWdCO0lBQ04sWUFBWSxDQUE4QjtJQUVwRCwwQ0FBMEM7SUFDMUMsSUFBVyxXQUFXO1FBQ3BCLElBQUksQ0FBQyxJQUFJLENBQUMsWUFBWSxFQUFFLENBQUM7WUFDdkIsTUFBTSxJQUFJLElBQUksQ0FBQyxlQUFlLENBQUMsSUFBQSxzQkFBRyxFQUFBLCtCQUErQixFQUFFLHVEQUF1RCxFQUFFLElBQUksQ0FBQyxDQUFDO1FBQ3BJLENBQUM7UUFDRCxPQUFPLElBQUksQ0FBQyxZQUFZLENBQUM7S0FDMUI7O0FBWkgsa0NBYUM7QUE4SUQ7O0dBRUc7QUFDSCxJQUFZLFdBVVg7QUFWRCxXQUFZLFdBQVc7SUFDckI7O09BRUc7SUFDSCw4QkFBZSxDQUFBO0lBRWY7O09BRUc7SUFDSCxnQ0FBaUIsQ0FBQTtBQUNuQixDQUFDLEVBVlcsV0FBVywyQkFBWCxXQUFXLFFBVXRCO0FBRUQ7O0dBRUc7QUFDSCxJQUFZLFVBVVg7QUFWRCxXQUFZLFVBQVU7SUFDcEI7O09BRUc7SUFDSCxtQ0FBcUIsQ0FBQTtJQUVyQjs7T0FFRztJQUNILGlDQUFtQixDQUFBO0FBQ3JCLENBQUMsRUFWVyxVQUFVLDBCQUFWLFVB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