cdk-dms-replication 0.1.12 → 0.1.13

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/.jsii CHANGED
@@ -3580,7 +3580,7 @@
3580
3580
  },
3581
3581
  "name": "cdk-dms-replication",
3582
3582
  "readme": {
3583
- "markdown": "# cdk-dms-replication\n\n[![npm version](https://badge.fury.io/js/cdk-dms-replication.svg)](https://badge.fury.io/js/cdk-dms-replication)\n[![PyPI version](https://badge.fury.io/py/cdk-dms-replication.svg)](https://badge.fury.io/py/cdk-dms-replication)\n[![build](https://github.com/kckempf/cdk-dms-replication/actions/workflows/build.yml/badge.svg)](https://github.com/kckempf/cdk-dms-replication/actions/workflows/build.yml)\n\nL3 CDK constructs for [Amazon Database Migration Service (DMS)](https://aws.amazon.com/dms/). Provision a complete migration pipeline — replication instance, endpoints, and task — in a few lines of code, with secure defaults and full support for all DMS-supported engines and migration patterns.\n\n## Features\n\n- **All migration patterns** — full load, CDC, and full-load-and-CDC\n- **Classic and Serverless** — `DmsMigrationPipeline` (fixed instance) or `DmsServerlessPipeline` (auto-scales DCUs)\n- **All DMS engines** — MySQL, PostgreSQL, Oracle, SQL Server, SAP ASE, IBM Db2, MongoDB, DocumentDB, S3, DynamoDB, Redshift, Kinesis, Kafka, OpenSearch, Neptune, Redis\n- **Secure defaults** — replication instance placed in private subnets, KMS encryption at rest, security group auto-created\n- **Fluent builders** — `TableMappings` and `TaskSettings` builders produce the JSON DMS expects without hand-crafting strings\n- **Multi-language** — TypeScript, Python, Java, .NET, Go (via JSII)\n- **Escape hatches** — pass existing endpoints or a pre-existing replication instance\n\n## Installation\n\n### TypeScript / JavaScript\n\n```bash\nnpm install cdk-dms-replication\n```\n\n### Python\n\n```bash\npip install cdk-dms-replication\n```\n\n### Java\n\n```xml\n<dependency>\n <groupId>io.github.kckempf</groupId>\n <artifactId>cdk-dms-replication</artifactId>\n <version>VERSION</version>\n</dependency>\n```\n\n### .NET\n\n```bash\ndotnet add package KcKempf.CdkDmsReplication\n```\n\n### Go\n\n```bash\ngo get github.com/kckempf/cdk-dms-replication-go\n```\n\n---\n\n## Quick start\n\n### Classic pipeline (fixed replication instance)\n\n`DmsMigrationPipeline` provisions a replication instance, both endpoints, and a replication task in one construct.\n\n```typescript\nimport * as cdk from 'aws-cdk-lib';\nimport * as ec2 from 'aws-cdk-lib/aws-ec2';\nimport {\n DmsMigrationPipeline,\n EndpointEngine,\n MigrationType,\n TableMappings,\n} from 'cdk-dms-replication';\n\nconst app = new cdk.App();\nconst stack = new cdk.Stack(app, 'MigrationStack');\n\nconst vpc = ec2.Vpc.fromLookup(stack, 'Vpc', { isDefault: false });\n\nnew DmsMigrationPipeline(stack, 'Pipeline', {\n vpc,\n migrationType: MigrationType.FULL_LOAD_AND_CDC,\n\n sourceEndpoint: {\n engine: EndpointEngine.MYSQL,\n serverName: 'mysql.internal.example.com',\n port: 3306,\n username: 'dms_user',\n password: cdk.SecretValue.secretsManager('mysql-dms-password'),\n databaseName: 'orders',\n },\n\n targetEndpoint: {\n engine: EndpointEngine.AURORA_POSTGRESQL,\n serverName: cluster.clusterEndpoint.hostname,\n port: 5432,\n username: 'dms_user',\n password: cdk.SecretValue.secretsManager('aurora-dms-password'),\n databaseName: 'orders',\n },\n\n tableMappings: new TableMappings()\n .includeSchema('public')\n .excludeTable('public', 'audit_log')\n .toJson(),\n});\n```\n\n> **Note:** The `tableMappings` default (when omitted) is to include all tables in all schemas.\n\n### Serverless pipeline (auto-scaling)\n\n`DmsServerlessPipeline` uses DMS Serverless, which automatically scales capacity (measured in DMS Capacity Units — DCUs) between a configurable minimum and maximum. There is no replication instance to size or manage.\n\n```typescript\nimport {\n DmsServerlessPipeline,\n EndpointEngine,\n MigrationType,\n} from 'cdk-dms-replication';\n\nnew DmsServerlessPipeline(stack, 'Pipeline', {\n vpc,\n maxCapacityUnits: 16, // required; valid values: 1,2,4,8,16,32,64,128,192,256,384\n minCapacityUnits: 2, // optional; DMS auto-determines if omitted\n migrationType: MigrationType.FULL_LOAD_AND_CDC,\n\n sourceEndpoint: {\n engine: EndpointEngine.MYSQL,\n serverName: 'mysql.internal.example.com',\n port: 3306,\n username: 'dms_user',\n password: cdk.SecretValue.secretsManager('mysql-dms-password'),\n databaseName: 'orders',\n },\n\n targetEndpoint: {\n engine: EndpointEngine.AURORA_POSTGRESQL,\n serverName: cluster.clusterEndpoint.hostname,\n port: 5432,\n username: 'dms_user',\n password: cdk.SecretValue.secretsManager('aurora-dms-password'),\n databaseName: 'orders',\n },\n});\n```\n\n> **CDC start/stop position limitation:** `DmsServerlessPipeline` does not support `cdcStartPosition` or `cdcStartTime` at the CloudFormation level. To start replication from a specific LSN or timestamp, call the [`StartReplication` API](https://docs.aws.amazon.com/dms/latest/APIReference/API_StartReplication.html) after the config is created.\n\n---\n\n## Migration patterns\n\n### Full load\n\nMigrates all existing data once, then stops.\n\n```typescript\nnew DmsMigrationPipeline(stack, 'Pipeline', {\n vpc,\n migrationType: MigrationType.FULL_LOAD,\n sourceEndpoint: { ... },\n targetEndpoint: { ... },\n});\n```\n\n### CDC only\n\nReplicates ongoing changes starting from a specific position or time. The target must already be seeded with data.\n\n```typescript\nnew DmsMigrationPipeline(stack, 'Pipeline', {\n vpc,\n migrationType: MigrationType.CDC,\n cdcStartPosition: 'mysql-bin-changelog.000024:373', // binlog position\n // — or —\n cdcStartTime: '2024-01-01T00:00:00Z', // ISO-8601 timestamp\n sourceEndpoint: { ... },\n targetEndpoint: { ... },\n});\n```\n\n### Full load then CDC\n\nMigrates existing data, then automatically switches to continuous replication.\n\n```typescript\nnew DmsMigrationPipeline(stack, 'Pipeline', {\n vpc,\n migrationType: MigrationType.FULL_LOAD_AND_CDC,\n sourceEndpoint: { ... },\n targetEndpoint: { ... },\n});\n```\n\n---\n\n## Endpoint examples\n\n### MySQL / MariaDB / Aurora MySQL\n\n```typescript\nsourceEndpoint: {\n engine: EndpointEngine.MYSQL, // or MARIADB, AURORA_MYSQL\n serverName: 'mysql.example.com',\n port: 3306,\n username: 'dms_user',\n password: cdk.SecretValue.secretsManager('db-secret'),\n databaseName: 'mydb',\n mySqlSettings: {\n parallelLoadThreads: 4,\n serverTimezone: 'UTC',\n },\n},\n```\n\n### PostgreSQL / Aurora PostgreSQL\n\n```typescript\nsourceEndpoint: {\n engine: EndpointEngine.POSTGRES, // or AURORA_POSTGRESQL\n serverName: 'pg.example.com',\n port: 5432,\n username: 'dms_user',\n password: cdk.SecretValue.secretsManager('db-secret'),\n databaseName: 'appdb',\n postgreSqlSettings: {\n captureDdls: true,\n slotName: 'dms_replication_slot',\n pluginName: PostgresCdcPlugin.PG_LOGICAL,\n heartbeatEnable: true,\n },\n},\n```\n\n### Oracle\n\n```typescript\nsourceEndpoint: {\n engine: EndpointEngine.ORACLE,\n serverName: 'oracle.example.com',\n port: 1521,\n username: 'dms_user',\n password: cdk.SecretValue.secretsManager('oracle-secret'),\n databaseName: 'ORCL',\n oracleSettings: {\n addSupplementalLogging: true,\n useLogminerReader: true, // true = LogMiner, false = BinaryReader\n },\n},\n```\n\n### SQL Server\n\n```typescript\nsourceEndpoint: {\n engine: EndpointEngine.SQLSERVER,\n serverName: 'sqlserver.example.com',\n port: 1433,\n username: 'dms_user',\n password: cdk.SecretValue.secretsManager('sqlserver-secret'),\n databaseName: 'AdventureWorks',\n sqlServerSettings: {\n readBackupOnly: false,\n safeguardPolicy: SqlServerSafeguardPolicy.RELY_ON_SQL_SERVER_REPLICATION_AGENT,\n },\n},\n```\n\n### MongoDB / DocumentDB\n\n```typescript\nsourceEndpoint: {\n engine: EndpointEngine.MONGODB, // or DOCDB\n serverName: 'mongo.example.com',\n port: 27017,\n username: 'dms_user',\n password: cdk.SecretValue.secretsManager('mongo-secret'),\n mongoDbSettings: {\n authType: MongoAuthType.PASSWORD,\n authMechanism: MongoAuthMechanism.SCRAM_SHA_1,\n nestingLevel: MongoNestingLevel.ONE,\n },\n},\n```\n\n### Amazon S3 (source or target)\n\n```typescript\n// As a target\ntargetEndpoint: {\n engine: EndpointEngine.S3,\n s3Settings: {\n bucketName: 'my-migration-data',\n bucketFolder: 'dms-output',\n serviceAccessRoleArn: s3Role.roleArn,\n dataFormat: S3DataFormat.PARQUET,\n parquetVersion: ParquetVersion.PARQUET_2_0,\n datePartitionEnabled: true,\n datePartitionSequence: DatePartitionSequence.YYYYMMDD,\n encryptionMode: EncryptionMode.SSE_KMS,\n serverSideEncryptionKmsKeyId: myKey.keyArn,\n },\n},\n```\n\n### Amazon Redshift\n\n```typescript\ntargetEndpoint: {\n engine: EndpointEngine.REDSHIFT,\n serverName: 'my-cluster.abc123.us-east-1.redshift.amazonaws.com',\n port: 5439,\n username: 'dms_user',\n password: cdk.SecretValue.secretsManager('redshift-secret'),\n databaseName: 'dev',\n redshiftSettings: {\n bucketName: 'my-redshift-staging',\n serviceAccessRoleArn: redshiftRole.roleArn,\n encryptionMode: EncryptionMode.SSE_KMS,\n serverSideEncryptionKmsKeyId: myKey.keyArn,\n truncateColumns: true,\n emptyAsNull: true,\n },\n},\n```\n\n### Amazon Kinesis Data Streams\n\n```typescript\ntargetEndpoint: {\n engine: EndpointEngine.KINESIS,\n kinesisSettings: {\n streamArn: stream.streamArn,\n serviceAccessRoleArn: kinesisRole.roleArn,\n messageFormat: MessageFormat.JSON,\n includeTransactionDetails: true,\n includeTableAlterOperations: true,\n },\n},\n```\n\n### Apache Kafka / Amazon MSK\n\n```typescript\ntargetEndpoint: {\n engine: EndpointEngine.KAFKA,\n kafkaSettings: {\n broker: 'b-1.my-cluster.abc123.kafka.us-east-1.amazonaws.com:9092',\n topic: 'dms-changes',\n messageFormat: MessageFormat.JSON,\n securityProtocol: KafkaSecurityProtocol.SASL_SSL,\n saslUsername: 'dms_user',\n saslPassword: cdk.SecretValue.secretsManager('kafka-sasl-password'),\n },\n},\n```\n\n### Amazon OpenSearch Service\n\n```typescript\ntargetEndpoint: {\n engine: EndpointEngine.OPENSEARCH,\n openSearchSettings: {\n endpointUri: 'https://search-my-domain.us-east-1.es.amazonaws.com',\n serviceAccessRoleArn: openSearchRole.roleArn,\n fullLoadErrorPercentage: 10,\n errorRetryDuration: 300,\n },\n},\n```\n\n### Amazon Neptune\n\n```typescript\ntargetEndpoint: {\n engine: EndpointEngine.NEPTUNE,\n neptuneSettings: {\n s3BucketName: 'my-neptune-staging',\n s3BucketFolder: 'dms',\n serviceAccessRoleArn: neptuneRole.roleArn,\n iamAuthEnabled: true,\n },\n},\n```\n\n### Amazon DynamoDB\n\n```typescript\ntargetEndpoint: {\n engine: EndpointEngine.DYNAMODB,\n dynamoDbSettings: {\n serviceAccessRoleArn: dynamoRole.roleArn,\n },\n},\n```\n\n---\n\n## Table mappings\n\nUse the `TableMappings` fluent builder to control which tables are migrated and how they are named on the target.\n\n### Selection rules\n\n```typescript\nnew TableMappings()\n .includeSchema('public') // include all tables in 'public'\n .includeSchema('%') // include all schemas (wildcard)\n .excludeTable('public', 'audit_log') // exclude a specific table\n .excludeSchema('internal') // exclude an entire schema\n .explicitTable('public', 'orders') // migrate only this one table\n .toJson()\n```\n\n### Transformation rules\n\n```typescript\nnew TableMappings()\n .includeSchema('%')\n .renameSchema('legacy', 'v2') // rename schema on target\n .toLowerCaseTable('public', '%') // lowercase all table names\n .toUpperCaseSchema('%') // uppercase all schema names\n .addPrefixToTable('public', '%', 'migrated_')\n .renameTable('public', 'usr', 'users') // rename a specific table\n .renameColumn('public', 'orders', 'cust_id', 'customer_id')\n .removeColumn('public', 'orders', 'internal_notes')\n .toJson()\n```\n\n### Adding columns\n\n```typescript\nnew TableMappings()\n .includeSchema('public')\n .addColumn('public', 'orders', {\n columnName: 'migrated_at',\n columnType: ColumnDataType.DATETIME,\n expression: '$timestamp', // DMS built-in expression\n })\n .addColumn('public', 'orders', {\n columnName: 'migration_version',\n columnType: ColumnDataType.STRING,\n columnLength: 10,\n columnValue: 'v2.0', // constant value\n })\n .toJson()\n```\n\n---\n\n## Task settings\n\nUse `TaskSettings` to tune LOB handling, error behaviour, full-load parallelism, and CDC batching.\n\n```typescript\nimport { TaskSettings, LobMode, ErrorAction, LoggingLevel } from 'cdk-dms-replication';\n\nconst settings = new TaskSettings()\n // LOB handling\n .withLobMode(LobMode.LIMITED_LOB, 64) // truncate LOBs at 64 KB\n\n // Full load tuning\n .withFullLoadSubTasks(16) // 16 parallel table loads\n .withTargetTablePrepMode('DROP_AND_CREATE')\n .withCommitRate(50000) // commit every 50k rows\n\n // CDC batch apply\n .withBatchApply(true, 5, 60) // batch changes, 5–60 second window\n\n // Error handling\n .withDataErrorPolicy(ErrorAction.IGNORE_RECORD, 1000)\n .withRecovery(-1, 5) // unlimited retries, 5s interval\n\n // Logging\n .withLogging('SOURCE_UNLOAD', LoggingLevel.LOGGER_SEVERITY_DEBUG)\n .withLogging('TARGET_LOAD', LoggingLevel.LOGGER_SEVERITY_DEFAULT)\n .toJson();\n\nnew DmsMigrationPipeline(stack, 'Pipeline', {\n vpc,\n migrationType: MigrationType.FULL_LOAD_AND_CDC,\n taskSettings: settings,\n sourceEndpoint: { ... },\n targetEndpoint: { ... },\n});\n```\n\n---\n\n## Replication instance options\n\n```typescript\nnew DmsMigrationPipeline(stack, 'Pipeline', {\n vpc,\n migrationType: MigrationType.FULL_LOAD_AND_CDC,\n\n // Instance sizing (default: R6I_LARGE)\n replicationInstanceClass: ReplicationInstanceClass.R6I_4XLARGE,\n allocatedStorage: 500, // GB\n\n // High availability\n multiAz: true,\n\n // Encryption — bring your own KMS key\n encryptionKey: myKmsKey,\n\n // Subnet placement\n vpcSubnets: {\n subnetType: ec2.SubnetType.PRIVATE_WITH_EGRESS,\n },\n\n sourceEndpoint: { ... },\n targetEndpoint: { ... },\n});\n```\n\n---\n\n## Using lower-level constructs directly\n\nIf you need more control, use the individual constructs and wire them together yourself.\n\n```typescript\nimport {\n DmsReplicationInstance,\n DmsEndpoint,\n DmsReplicationTask,\n EndpointType,\n EndpointEngine,\n MigrationType,\n TableMappings,\n} from 'cdk-dms-replication';\n\n// 1. Replication instance\nconst instance = new DmsReplicationInstance(stack, 'Instance', {\n vpc,\n replicationInstanceClass: ReplicationInstanceClass.R6I_LARGE,\n multiAz: true,\n});\n\n// Allow the source DB security group to accept connections from DMS\ninstance.allowInboundFrom(\n ec2.Peer.securityGroupId(myDbSg.securityGroupId),\n ec2.Port.tcp(3306),\n);\n\n// 2. Endpoints\nconst source = new DmsEndpoint(stack, 'Source', {\n endpointType: EndpointType.SOURCE,\n engine: EndpointEngine.MYSQL,\n serverName: 'mysql.example.com',\n port: 3306,\n username: 'dms_user',\n password: cdk.SecretValue.secretsManager('db-secret'),\n databaseName: 'mydb',\n});\n\nconst target = new DmsEndpoint(stack, 'Target', {\n endpointType: EndpointType.TARGET,\n engine: EndpointEngine.S3,\n s3Settings: {\n bucketName: 'my-bucket',\n serviceAccessRoleArn: s3Role.roleArn,\n },\n});\n\n// 3. Replication task\nnew DmsReplicationTask(stack, 'Task', {\n replicationInstanceArn: instance.replicationInstanceArn,\n sourceEndpoint: source,\n targetEndpoint: target,\n migrationType: MigrationType.FULL_LOAD_AND_CDC,\n tableMappings: new TableMappings().includeSchema('public').toJson(),\n});\n```\n\n---\n\n## Using existing endpoints\n\nBring your own endpoints if they already exist (e.g., created outside CDK or in a different stack):\n\n```typescript\nimport { IDmsEndpoint } from 'cdk-dms-replication';\n\n// Reference an existing endpoint by ARN\nconst existingSource: IDmsEndpoint = {\n endpointArn: 'arn:aws:dms:us-east-1:123456789012:endpoint:ABCDEF',\n};\n\nnew DmsMigrationPipeline(stack, 'Pipeline', {\n vpc,\n migrationType: MigrationType.CDC,\n existingSourceEndpoint: existingSource,\n targetEndpoint: {\n engine: EndpointEngine.S3,\n s3Settings: { ... },\n },\n});\n```\n\n---\n\n## Secrets Manager integration\n\nFor production workloads, store credentials in AWS Secrets Manager and let DMS retrieve them directly (no plaintext in CloudFormation):\n\n```typescript\nsourceEndpoint: {\n engine: EndpointEngine.MYSQL,\n serverName: 'mysql.example.com',\n port: 3306,\n mySqlSettings: {\n secretsManagerSecretId: 'arn:aws:secretsmanager:us-east-1:123456789012:secret:dms/mysql-abc123',\n secretsManagerAccessRoleArn: dmsSecretsRole.roleArn,\n },\n},\n```\n\nThe secret must contain `username` and `password` keys. See [Using AWS Secrets Manager to access database credentials](https://docs.aws.amazon.com/dms/latest/userguide/CHAP_Security.html#security-iam-secretsmanager) for the required secret format and IAM permissions.\n\n---\n\n## Cross-account migrations\n\nDMS supports migrating data between AWS accounts. The replication instance always lives in one account (the **DMS account**) and connects to source and target databases over the network, regardless of which account owns them.\n\n### Prerequisites\n\nTwo things must be true before the construct can help:\n\n1. **Network connectivity** — The replication instance's VPC must be able to reach both endpoints. Establish this with [VPC peering](https://docs.aws.amazon.com/vpc/latest/peering/what-is-vpc-peering.html), [AWS Transit Gateway](https://docs.aws.amazon.com/vpc/latest/tgw/what-is-transit-gateway.html), or [AWS PrivateLink](https://docs.aws.amazon.com/vpc/latest/privatelink/what-is-privatelink.html) before deploying. The construct has no visibility into routing — it will synthesise correctly regardless, but the task will fail at runtime if the endpoints are unreachable.\n\n2. **IAM cross-account trust** — For AWS-managed targets (S3, Kinesis, Redshift, DynamoDB, etc.) owned by a different account, DMS needs an IAM role in the **target account** that trusts the DMS service principal in the **DMS account**.\n\n### CDK stack setup\n\nCDK cannot pass constructs like `ec2.IVpc` across account boundaries. Use `Vpc.fromLookup` in the DMS account stack to reference the VPC by ID:\n\n```typescript\n// DMS account stack (e.g. account 111111111111)\nconst vpc = ec2.Vpc.fromLookup(stack, 'Vpc', {\n vpcId: 'vpc-0abc1234def567890',\n});\n```\n\n### Database endpoints (any engine)\n\nFor source or target databases running in another account, provide the hostname that is reachable from the replication instance's VPC (private IP, private DNS name, or VPC peering DNS). No special construct configuration is needed beyond what you would use for a same-account migration.\n\n```typescript\nnew DmsMigrationPipeline(stack, 'Pipeline', {\n vpc,\n migrationType: MigrationType.FULL_LOAD_AND_CDC,\n\n // Source DB in account 222222222222, reachable via Transit Gateway\n sourceEndpoint: {\n engine: EndpointEngine.ORACLE,\n serverName: '10.1.2.3', // private IP from peered VPC\n port: 1521,\n username: 'dms_user',\n password: cdk.SecretValue.secretsManager('oracle-dms-secret'),\n databaseName: 'ORCL',\n },\n\n // Target in the same DMS account — normal config\n targetEndpoint: {\n engine: EndpointEngine.AURORA_POSTGRESQL,\n serverName: cluster.clusterEndpoint.hostname,\n port: 5432,\n username: 'dms_user',\n password: cdk.SecretValue.secretsManager('aurora-dms-secret'),\n databaseName: 'mydb',\n },\n});\n```\n\n### AWS-managed targets in another account (S3, Kinesis, Redshift, etc.)\n\nWhen the target service lives in a different account, create an IAM role in the **target account** that DMS (running in the DMS account) can assume. Pass its ARN via `serviceAccessRoleArn`.\n\n**Step 1 — Create the cross-account role in the target account (222222222222):**\n\n```typescript\n// In a stack deployed to the TARGET account (222222222222)\nconst crossAccountDmsRole = new iam.Role(targetStack, 'DmsCrossAccountRole', {\n assumedBy: new iam.CompositePrincipal(\n // Allow DMS in the DMS account to assume this role\n new iam.ArnPrincipal(`arn:aws:iam::111111111111:role/dms-vpc-role`),\n new iam.ServicePrincipal('dms.amazonaws.com'),\n ),\n inlinePolicies: {\n S3Access: new iam.PolicyDocument({\n statements: [\n new iam.PolicyStatement({\n actions: ['s3:PutObject', 's3:DeleteObject', 's3:ListBucket'],\n resources: [\n targetBucket.bucketArn,\n `${targetBucket.bucketArn}/*`,\n ],\n }),\n ],\n }),\n },\n});\n```\n\n**Step 2 — Reference the role ARN in the DMS account stack:**\n\n```typescript\n// In the DMS account stack (111111111111)\nnew DmsMigrationPipeline(stack, 'Pipeline', {\n vpc,\n migrationType: MigrationType.FULL_LOAD,\n sourceEndpoint: {\n engine: EndpointEngine.MYSQL,\n serverName: 'mysql.internal.example.com',\n port: 3306,\n username: 'dms_user',\n password: cdk.SecretValue.secretsManager('mysql-dms-secret'),\n databaseName: 'orders',\n },\n targetEndpoint: {\n engine: EndpointEngine.S3,\n s3Settings: {\n bucketName: 'target-account-bucket', // bucket in account 222222222222\n serviceAccessRoleArn: 'arn:aws:iam::222222222222:role/DmsCrossAccountRole',\n },\n },\n});\n```\n\nThe same pattern applies to Kinesis, Redshift, DynamoDB, and other AWS-managed targets — create the role in the target account, grant it the permissions that service needs, and pass its ARN to the relevant `serviceAccessRoleArn` field.\n\n### Cross-account Secrets Manager\n\nIf the database credentials are stored in Secrets Manager in the source account (222222222222) but DMS runs in a different account (111111111111):\n\n1. Add a [resource-based policy](https://docs.aws.amazon.com/secretsmanager/latest/userguide/auth-and-access_resource-based-policies.html) to the secret in account 222222222222 that allows the DMS account's role to call `secretsmanager:GetSecretValue`.\n2. Pass the full secret ARN and the cross-account access role ARN to the endpoint settings:\n\n```typescript\nsourceEndpoint: {\n engine: EndpointEngine.MYSQL,\n serverName: 'mysql.internal.example.com',\n port: 3306,\n mySqlSettings: {\n secretsManagerSecretId:\n 'arn:aws:secretsmanager:us-east-1:222222222222:secret:dms/mysql-abc123',\n secretsManagerAccessRoleArn:\n 'arn:aws:iam::111111111111:role/DmsSecretsManagerRole',\n },\n},\n```\n\n### Cross-account KMS encryption\n\nThe construct creates a KMS key in the DMS account for replication instance storage. If you need the replication instance to write to a KMS-encrypted target in another account, bring your own key and add a cross-account statement to its key policy:\n\n```typescript\n// Key in the DMS account (111111111111), with cross-account decrypt permission\nconst encryptionKey = new kms.Key(stack, 'ReplicationKey', {\n enableKeyRotation: true,\n policy: new iam.PolicyDocument({\n statements: [\n // Standard key admin/use permissions ...\n new iam.PolicyStatement({\n principals: [new iam.AccountPrincipal('222222222222')],\n actions: ['kms:Decrypt', 'kms:GenerateDataKey'],\n resources: ['*'],\n }),\n ],\n }),\n});\n\nnew DmsMigrationPipeline(stack, 'Pipeline', {\n vpc,\n encryptionKey,\n migrationType: MigrationType.FULL_LOAD_AND_CDC,\n sourceEndpoint: { ... },\n targetEndpoint: { ... },\n});\n```\n\n### Summary of cross-account responsibilities\n\n| Concern | Who sets it up | How to configure |\n|---------|---------------|-----------------|\n| Network connectivity | You (VPC peering / TGW / PrivateLink) | Prerequisite — no construct prop |\n| Database endpoint hostname | You | `serverName` — use private IP or private DNS |\n| Cross-account service role (S3, Kinesis, etc.) | You (role in target account) | `serviceAccessRoleArn` |\n| Cross-account Secrets Manager | You (resource policy on secret) | `secretsManagerSecretId` + `secretsManagerAccessRoleArn` |\n| Cross-account KMS | You (key policy) | `encryptionKey` (bring your own) |\n| Replication instance, subnet group, task | This construct | Fully managed |\n\n---\n\n## Observability\n\nA CloudWatch Logs log group is created by default. Customise the retention period or disable it:\n\n```typescript\nimport * as logs from 'aws-cdk-lib/aws-logs';\n\nnew DmsMigrationPipeline(stack, 'Pipeline', {\n vpc,\n migrationType: MigrationType.FULL_LOAD_AND_CDC,\n enableCloudWatchLogs: true,\n logRetention: logs.RetentionDays.THREE_MONTHS,\n sourceEndpoint: { ... },\n targetEndpoint: { ... },\n});\n```\n\n---\n\n## API reference\n\nFull API documentation is available in [API.md](https://github.com/kckempf/cdk-dms-replication/blob/main/API.md) and on [Construct Hub](https://constructs.dev/packages/cdk-dms-replication).\n\n---\n\n## Supported source engines\n\n| Engine | `EndpointEngine` value |\n|--------|----------------------|\n| MySQL | `MYSQL` |\n| Amazon Aurora (MySQL) | `AURORA_MYSQL` |\n| PostgreSQL | `POSTGRES` |\n| Amazon Aurora (PostgreSQL) | `AURORA_POSTGRESQL` |\n| Oracle | `ORACLE` |\n| Microsoft SQL Server | `SQLSERVER` |\n| MariaDB | `MARIADB` |\n| SAP ASE (Sybase) | `SAP_ASE` |\n| IBM Db2 LUW | `IBM_DB2` |\n| IBM Db2 for z/OS | `IBM_DB2_ZOS` |\n| MongoDB | `MONGODB` |\n| Amazon DocumentDB | `DOCDB` |\n| Amazon S3 | `S3` |\n\n## Supported target engines\n\nAll source engines above, plus:\n\n| Engine | `EndpointEngine` value |\n|--------|----------------------|\n| Amazon S3 | `S3` |\n| Amazon DynamoDB | `DYNAMODB` |\n| Amazon Redshift | `REDSHIFT` |\n| Amazon Kinesis Data Streams | `KINESIS` |\n| Apache Kafka / Amazon MSK | `KAFKA` |\n| Amazon OpenSearch Service | `OPENSEARCH` |\n| Amazon Neptune | `NEPTUNE` |\n| Amazon ElastiCache for Redis | `REDIS` |\n\n---\n\n## Security considerations\n\n- The replication instance is placed in **private subnets** by default. Set `publiclyAccessible: true` only if required.\n- Storage is encrypted at rest using a **KMS customer-managed key** (auto-created if you don't provide one).\n- **Do not use the `password` prop in production.** The resolved value is written as plaintext into the CloudFormation template and state file. Use Secrets Manager instead: set `secretsManagerSecretId` and `secretsManagerAccessRoleArn` in the engine-specific settings and omit `password` entirely.\n- Grant DMS only the minimum IAM permissions required for each target engine.\n- For CDC with PostgreSQL, the replication user needs the `rds_replication` role (RDS) or `REPLICATION` privilege (self-managed).\n- For CDC with Oracle, supplemental logging must be enabled on the source database.\n- **`dms-vpc-role` and `dms-cloudwatch-logs-role`** are account-level IAM roles required by DMS. When `createDmsServiceRoles` is `true` (the default), they are created idempotently via a custom resource — if they already exist in the account (created by another stack or manually), the existing roles are silently reused and their trust policies corrected if necessary. The roles are created with `RemovalPolicy.RETAIN` and are **not** lifecycle-managed by the deploying stack; they will not be deleted when the stack is destroyed. If you require the roles to be lifecycle-managed, create them in a dedicated stack and set `createDmsServiceRoles: false`.\n\n---\n\n## Contributing\n\nBug reports and pull requests are welcome. Please open an issue at [github.com/kckempf/cdk-dms-replication/issues](https://github.com/kckempf/cdk-dms-replication/issues) before starting significant work.\n\n---\n\n## Author\n\n[Kevin Kempf](https://github.com/kckempf)\n\n---\n\n## License\n\nApache-2.0 — see [LICENSE](https://github.com/kckempf/cdk-dms-replication/blob/main/LICENSE)\n"
3583
+ "markdown": "# cdk-dms-replication\n\n[![npm version](https://badge.fury.io/js/cdk-dms-replication.svg)](https://badge.fury.io/js/cdk-dms-replication)\n[![PyPI version](https://badge.fury.io/py/cdk-dms-replication.svg)](https://badge.fury.io/py/cdk-dms-replication)\n[![build](https://github.com/kckempf/cdk-dms-replication/actions/workflows/build.yml/badge.svg)](https://github.com/kckempf/cdk-dms-replication/actions/workflows/build.yml)\n\nL3 CDK constructs for [Amazon Database Migration Service (DMS)](https://aws.amazon.com/dms/). Provision a complete migration pipeline — replication instance, endpoints, and task — in a few lines of code, with secure defaults and full support for all DMS-supported engines and migration patterns.\n\n## Features\n\n- **All migration patterns** — full load, CDC, and full-load-and-CDC\n- **Classic and Serverless** — `DmsMigrationPipeline` (fixed instance) or `DmsServerlessPipeline` (auto-scales DCUs)\n- **All DMS engines** — MySQL, PostgreSQL, Oracle, SQL Server, SAP ASE, IBM Db2, MongoDB, DocumentDB, S3, DynamoDB, Redshift, Kinesis, Kafka, OpenSearch, Neptune, Redis\n- **Secure defaults** — replication instance placed in private subnets, KMS encryption at rest, security group auto-created\n- **Fluent builders** — `TableMappings` and `TaskSettings` builders produce the JSON DMS expects without hand-crafting strings\n- **Multi-language** — TypeScript, Python, Java, .NET, Go (via JSII)\n- **Escape hatches** — pass existing endpoints or a pre-existing replication instance\n\n## Installation\n\n### TypeScript / JavaScript\n\n```bash\nnpm install cdk-dms-replication\n```\n\n### Python\n\n```bash\npip install cdk-dms-replication\n```\n\n### Java\n\n```xml\n<dependency>\n <groupId>io.github.kckempf</groupId>\n <artifactId>cdk-dms-replication</artifactId>\n <version>VERSION</version>\n</dependency>\n```\n\n### .NET\n\n```bash\ndotnet add package KcKempf.CdkDmsReplication\n```\n\n### Go\n\n```bash\ngo get github.com/kckempf/cdk-dms-replication-go\n```\n\n---\n\n## Quick start\n\n### Classic pipeline (fixed replication instance)\n\n`DmsMigrationPipeline` provisions a replication instance, both endpoints, and a replication task in one construct.\n\n```typescript\nimport * as cdk from 'aws-cdk-lib';\nimport * as ec2 from 'aws-cdk-lib/aws-ec2';\nimport {\n DmsMigrationPipeline,\n EndpointEngine,\n MigrationType,\n TableMappings,\n} from 'cdk-dms-replication';\n\nconst app = new cdk.App();\nconst stack = new cdk.Stack(app, 'MigrationStack');\n\nconst vpc = ec2.Vpc.fromLookup(stack, 'Vpc', { isDefault: false });\n\nnew DmsMigrationPipeline(stack, 'Pipeline', {\n vpc,\n migrationType: MigrationType.FULL_LOAD_AND_CDC,\n\n sourceEndpoint: {\n engine: EndpointEngine.MYSQL,\n serverName: 'mysql.internal.example.com',\n port: 3306,\n username: 'dms_user',\n password: cdk.SecretValue.secretsManager('mysql-dms-password'),\n databaseName: 'orders',\n },\n\n targetEndpoint: {\n engine: EndpointEngine.AURORA_POSTGRESQL,\n serverName: cluster.clusterEndpoint.hostname,\n port: 5432,\n username: 'dms_user',\n password: cdk.SecretValue.secretsManager('aurora-dms-password'),\n databaseName: 'orders',\n },\n\n tableMappings: new TableMappings()\n .includeSchema('public')\n .excludeTable('public', 'audit_log')\n .toJson(),\n});\n```\n\n> **Note:** The `tableMappings` default (when omitted) is to include all tables in all schemas.\n\n### Serverless pipeline (auto-scaling)\n\n`DmsServerlessPipeline` uses DMS Serverless, which automatically scales capacity (measured in DMS Capacity Units — DCUs) between a configurable minimum and maximum. There is no replication instance to size or manage.\n\n```typescript\nimport {\n DmsServerlessPipeline,\n EndpointEngine,\n MigrationType,\n} from 'cdk-dms-replication';\n\nnew DmsServerlessPipeline(stack, 'Pipeline', {\n vpc,\n maxCapacityUnits: 16, // required; valid values: 1,2,4,8,16,32,64,128,192,256,384\n minCapacityUnits: 2, // optional; DMS auto-determines if omitted\n migrationType: MigrationType.FULL_LOAD_AND_CDC,\n\n sourceEndpoint: {\n engine: EndpointEngine.MYSQL,\n serverName: 'mysql.internal.example.com',\n port: 3306,\n username: 'dms_user',\n password: cdk.SecretValue.secretsManager('mysql-dms-password'),\n databaseName: 'orders',\n },\n\n targetEndpoint: {\n engine: EndpointEngine.AURORA_POSTGRESQL,\n serverName: cluster.clusterEndpoint.hostname,\n port: 5432,\n username: 'dms_user',\n password: cdk.SecretValue.secretsManager('aurora-dms-password'),\n databaseName: 'orders',\n },\n});\n```\n\n> **CDC start/stop position limitation:** `DmsServerlessPipeline` does not support `cdcStartPosition` or `cdcStartTime` at the CloudFormation level. To start replication from a specific LSN or timestamp, call the [`StartReplication` API](https://docs.aws.amazon.com/dms/latest/APIReference/API_StartReplication.html) after the config is created.\n\n---\n\n## Migration patterns\n\n### Full load\n\nMigrates all existing data once, then stops.\n\n```typescript\nnew DmsMigrationPipeline(stack, 'Pipeline', {\n vpc,\n migrationType: MigrationType.FULL_LOAD,\n sourceEndpoint: { ... },\n targetEndpoint: { ... },\n});\n```\n\n### CDC only\n\nReplicates ongoing changes starting from a specific position or time. The target must already be seeded with data.\n\n```typescript\nnew DmsMigrationPipeline(stack, 'Pipeline', {\n vpc,\n migrationType: MigrationType.CDC,\n cdcStartPosition: 'mysql-bin-changelog.000024:373', // binlog position\n // — or —\n cdcStartTime: '2024-01-01T00:00:00Z', // ISO-8601 timestamp\n sourceEndpoint: { ... },\n targetEndpoint: { ... },\n});\n```\n\n### Full load then CDC\n\nMigrates existing data, then automatically switches to continuous replication.\n\n```typescript\nnew DmsMigrationPipeline(stack, 'Pipeline', {\n vpc,\n migrationType: MigrationType.FULL_LOAD_AND_CDC,\n sourceEndpoint: { ... },\n targetEndpoint: { ... },\n});\n```\n\n---\n\n## Endpoint examples\n\n### MySQL / MariaDB / Aurora MySQL\n\n```typescript\nsourceEndpoint: {\n engine: EndpointEngine.MYSQL, // or MARIADB, AURORA_MYSQL\n serverName: 'mysql.example.com',\n port: 3306,\n username: 'dms_user',\n password: cdk.SecretValue.secretsManager('db-secret'),\n databaseName: 'mydb',\n mySqlSettings: {\n parallelLoadThreads: 4,\n serverTimezone: 'UTC',\n },\n},\n```\n\n### PostgreSQL / Aurora PostgreSQL\n\n```typescript\nsourceEndpoint: {\n engine: EndpointEngine.POSTGRES, // or AURORA_POSTGRESQL\n serverName: 'pg.example.com',\n port: 5432,\n username: 'dms_user',\n password: cdk.SecretValue.secretsManager('db-secret'),\n databaseName: 'appdb',\n postgreSqlSettings: {\n captureDdls: true,\n slotName: 'dms_replication_slot',\n pluginName: PostgresCdcPlugin.PG_LOGICAL,\n heartbeatEnable: true,\n },\n},\n```\n\n### Oracle\n\n```typescript\nsourceEndpoint: {\n engine: EndpointEngine.ORACLE,\n serverName: 'oracle.example.com',\n port: 1521,\n username: 'dms_user',\n password: cdk.SecretValue.secretsManager('oracle-secret'),\n databaseName: 'ORCL',\n oracleSettings: {\n addSupplementalLogging: true,\n useLogminerReader: true, // true = LogMiner, false = BinaryReader\n },\n},\n```\n\n### SQL Server\n\n```typescript\nsourceEndpoint: {\n engine: EndpointEngine.SQLSERVER,\n serverName: 'sqlserver.example.com',\n port: 1433,\n username: 'dms_user',\n password: cdk.SecretValue.secretsManager('sqlserver-secret'),\n databaseName: 'AdventureWorks',\n sqlServerSettings: {\n readBackupOnly: false,\n safeguardPolicy: SqlServerSafeguardPolicy.RELY_ON_SQL_SERVER_REPLICATION_AGENT,\n },\n},\n```\n\n### MongoDB / DocumentDB\n\n```typescript\nsourceEndpoint: {\n engine: EndpointEngine.MONGODB, // or DOCDB\n serverName: 'mongo.example.com',\n port: 27017,\n username: 'dms_user',\n password: cdk.SecretValue.secretsManager('mongo-secret'),\n mongoDbSettings: {\n authType: MongoAuthType.PASSWORD,\n authMechanism: MongoAuthMechanism.SCRAM_SHA_1,\n nestingLevel: MongoNestingLevel.ONE,\n },\n},\n```\n\n### Amazon S3 (source or target)\n\n```typescript\n// As a target\ntargetEndpoint: {\n engine: EndpointEngine.S3,\n s3Settings: {\n bucketName: 'my-migration-data',\n bucketFolder: 'dms-output',\n serviceAccessRoleArn: s3Role.roleArn,\n dataFormat: S3DataFormat.PARQUET,\n parquetVersion: ParquetVersion.PARQUET_2_0,\n datePartitionEnabled: true,\n datePartitionSequence: DatePartitionSequence.YYYYMMDD,\n encryptionMode: EncryptionMode.SSE_KMS,\n serverSideEncryptionKmsKeyId: myKey.keyArn,\n },\n},\n```\n\n### Amazon Redshift\n\n```typescript\ntargetEndpoint: {\n engine: EndpointEngine.REDSHIFT,\n serverName: 'my-cluster.abc123.us-east-1.redshift.amazonaws.com',\n port: 5439,\n username: 'dms_user',\n password: cdk.SecretValue.secretsManager('redshift-secret'),\n databaseName: 'dev',\n redshiftSettings: {\n bucketName: 'my-redshift-staging',\n serviceAccessRoleArn: redshiftRole.roleArn,\n encryptionMode: EncryptionMode.SSE_KMS,\n serverSideEncryptionKmsKeyId: myKey.keyArn,\n truncateColumns: true,\n emptyAsNull: true,\n },\n},\n```\n\n### Amazon Kinesis Data Streams\n\n```typescript\ntargetEndpoint: {\n engine: EndpointEngine.KINESIS,\n kinesisSettings: {\n streamArn: stream.streamArn,\n serviceAccessRoleArn: kinesisRole.roleArn,\n messageFormat: MessageFormat.JSON,\n includeTransactionDetails: true,\n includeTableAlterOperations: true,\n },\n},\n```\n\n### Apache Kafka / Amazon MSK\n\n```typescript\ntargetEndpoint: {\n engine: EndpointEngine.KAFKA,\n kafkaSettings: {\n broker: 'b-1.my-cluster.abc123.kafka.us-east-1.amazonaws.com:9092',\n topic: 'dms-changes',\n messageFormat: MessageFormat.JSON,\n securityProtocol: KafkaSecurityProtocol.SASL_SSL,\n saslUsername: 'dms_user',\n saslPassword: cdk.SecretValue.secretsManager('kafka-sasl-password'),\n },\n},\n```\n\n### Amazon OpenSearch Service\n\n```typescript\ntargetEndpoint: {\n engine: EndpointEngine.OPENSEARCH,\n openSearchSettings: {\n endpointUri: 'https://search-my-domain.us-east-1.es.amazonaws.com',\n serviceAccessRoleArn: openSearchRole.roleArn,\n fullLoadErrorPercentage: 10,\n errorRetryDuration: 300,\n },\n},\n```\n\n### Amazon Neptune\n\n```typescript\ntargetEndpoint: {\n engine: EndpointEngine.NEPTUNE,\n neptuneSettings: {\n s3BucketName: 'my-neptune-staging',\n s3BucketFolder: 'dms',\n serviceAccessRoleArn: neptuneRole.roleArn,\n iamAuthEnabled: true,\n },\n},\n```\n\n### Amazon DynamoDB\n\n```typescript\ntargetEndpoint: {\n engine: EndpointEngine.DYNAMODB,\n dynamoDbSettings: {\n serviceAccessRoleArn: dynamoRole.roleArn,\n },\n},\n```\n\nDynamoDB targets also require an `object-mapping` rule per source table in the\ntable mappings. See [DynamoDB target (object mapping)](#dynamodb-target-object-mapping)\nbelow.\n\n---\n\n## Table mappings\n\nUse the `TableMappings` fluent builder to control which tables are migrated and how they are named on the target.\n\n### Selection rules\n\n```typescript\nnew TableMappings()\n .includeSchema('public') // include all tables in 'public'\n .includeSchema('%') // include all schemas (wildcard)\n .excludeTable('public', 'audit_log') // exclude a specific table\n .excludeSchema('internal') // exclude an entire schema\n .explicitTable('public', 'orders') // migrate only this one table\n .toJson()\n```\n\n### Transformation rules\n\n```typescript\nnew TableMappings()\n .includeSchema('%')\n .renameSchema('legacy', 'v2') // rename schema on target\n .toLowerCaseTable('public', '%') // lowercase all table names\n .toUpperCaseSchema('%') // uppercase all schema names\n .addPrefixToTable('public', '%', 'migrated_')\n .renameTable('public', 'usr', 'users') // rename a specific table\n .renameColumn('public', 'orders', 'cust_id', 'customer_id')\n .removeColumn('public', 'orders', 'internal_notes')\n .toJson()\n```\n\n### Adding columns\n\n```typescript\nnew TableMappings()\n .includeSchema('public')\n .addColumn('public', 'orders', {\n columnName: 'migrated_at',\n columnType: ColumnDataType.DATETIME,\n expression: '$timestamp', // DMS built-in expression\n })\n .addColumn('public', 'orders', {\n columnName: 'migration_version',\n columnType: ColumnDataType.STRING,\n columnLength: 10,\n columnValue: 'v2.0', // constant value\n })\n .toJson()\n```\n\n### DynamoDB target (object mapping)\n\nWhen the target endpoint is DynamoDB, DMS requires an `object-mapping` rule per\nsource table that defines the partition key (and optionally a sort key) and\nhow source columns become DynamoDB attributes. Use `mapToDynamoDb()` alongside\nthe usual `includeTable()`:\n\n```typescript\nnew TableMappings()\n .includeTable('public', 'orders')\n .mapToDynamoDb('public', 'orders', {\n targetTableName: 'Orders',\n partitionKey: {\n sourceColumn: 'order_id',\n targetAttributeName: 'OrderId',\n attributeSubType: DynamoDbAttributeSubType.STRING,\n },\n sortKey: {\n sourceColumn: 'created_at',\n targetAttributeName: 'CreatedAt',\n attributeSubType: DynamoDbAttributeSubType.STRING,\n },\n excludeColumns: ['internal_flag'],\n attributeMappings: [\n {\n sourceColumn: 'customer_id',\n targetAttributeName: 'CustomerId',\n attributeSubType: DynamoDbAttributeSubType.STRING,\n },\n ],\n })\n .toJson()\n```\n\nSource columns not listed in `attributeMappings` or `excludeColumns` are\nmigrated under their source column name.\n\n#### Composite key values\n\nFor DynamoDB single-table designs you often need composite keys built from a\nliteral prefix plus one or more source columns. Use `value` instead of\n`sourceColumn` to provide a raw DMS expression:\n\n```typescript\nnew TableMappings()\n .includeTable('public', 'orders')\n .mapToDynamoDb('public', 'orders', {\n targetTableName: 'AppTable',\n partitionKey: {\n value: 'CUSTOMER#${customer_id}',\n targetAttributeName: 'PK',\n attributeSubType: DynamoDbAttributeSubType.STRING,\n },\n sortKey: {\n value: 'ORDER#${order_id}',\n targetAttributeName: 'SK',\n attributeSubType: DynamoDbAttributeSubType.STRING,\n },\n })\n .toJson()\n```\n\nExactly one of `sourceColumn` or `value` must be set on each key/attribute\nmapping — the builder throws at synth time otherwise.\n\n---\n\n## Task settings\n\nUse `TaskSettings` to tune LOB handling, error behaviour, full-load parallelism, and CDC batching.\n\n```typescript\nimport { TaskSettings, LobMode, ErrorAction, LoggingLevel } from 'cdk-dms-replication';\n\nconst settings = new TaskSettings()\n // LOB handling\n .withLobMode(LobMode.LIMITED_LOB, 64) // truncate LOBs at 64 KB\n\n // Full load tuning\n .withFullLoadSubTasks(16) // 16 parallel table loads\n .withTargetTablePrepMode('DROP_AND_CREATE')\n .withCommitRate(50000) // commit every 50k rows\n\n // CDC batch apply\n .withBatchApply(true, 5, 60) // batch changes, 5–60 second window\n\n // Error handling\n .withDataErrorPolicy(ErrorAction.IGNORE_RECORD, 1000)\n .withRecovery(-1, 5) // unlimited retries, 5s interval\n\n // Logging\n .withLogging('SOURCE_UNLOAD', LoggingLevel.LOGGER_SEVERITY_DEBUG)\n .withLogging('TARGET_LOAD', LoggingLevel.LOGGER_SEVERITY_DEFAULT)\n .toJson();\n\nnew DmsMigrationPipeline(stack, 'Pipeline', {\n vpc,\n migrationType: MigrationType.FULL_LOAD_AND_CDC,\n taskSettings: settings,\n sourceEndpoint: { ... },\n targetEndpoint: { ... },\n});\n```\n\n---\n\n## Replication instance options\n\n```typescript\nnew DmsMigrationPipeline(stack, 'Pipeline', {\n vpc,\n migrationType: MigrationType.FULL_LOAD_AND_CDC,\n\n // Instance sizing (default: R6I_LARGE)\n replicationInstanceClass: ReplicationInstanceClass.R6I_4XLARGE,\n allocatedStorage: 500, // GB\n\n // High availability\n multiAz: true,\n\n // Encryption — bring your own KMS key\n encryptionKey: myKmsKey,\n\n // Subnet placement\n vpcSubnets: {\n subnetType: ec2.SubnetType.PRIVATE_WITH_EGRESS,\n },\n\n sourceEndpoint: { ... },\n targetEndpoint: { ... },\n});\n```\n\n---\n\n## Using lower-level constructs directly\n\nIf you need more control, use the individual constructs and wire them together yourself.\n\n```typescript\nimport {\n DmsReplicationInstance,\n DmsEndpoint,\n DmsReplicationTask,\n EndpointType,\n EndpointEngine,\n MigrationType,\n TableMappings,\n} from 'cdk-dms-replication';\n\n// 1. Replication instance\nconst instance = new DmsReplicationInstance(stack, 'Instance', {\n vpc,\n replicationInstanceClass: ReplicationInstanceClass.R6I_LARGE,\n multiAz: true,\n});\n\n// Allow the source DB security group to accept connections from DMS\ninstance.allowInboundFrom(\n ec2.Peer.securityGroupId(myDbSg.securityGroupId),\n ec2.Port.tcp(3306),\n);\n\n// 2. Endpoints\nconst source = new DmsEndpoint(stack, 'Source', {\n endpointType: EndpointType.SOURCE,\n engine: EndpointEngine.MYSQL,\n serverName: 'mysql.example.com',\n port: 3306,\n username: 'dms_user',\n password: cdk.SecretValue.secretsManager('db-secret'),\n databaseName: 'mydb',\n});\n\nconst target = new DmsEndpoint(stack, 'Target', {\n endpointType: EndpointType.TARGET,\n engine: EndpointEngine.S3,\n s3Settings: {\n bucketName: 'my-bucket',\n serviceAccessRoleArn: s3Role.roleArn,\n },\n});\n\n// 3. Replication task\nnew DmsReplicationTask(stack, 'Task', {\n replicationInstanceArn: instance.replicationInstanceArn,\n sourceEndpoint: source,\n targetEndpoint: target,\n migrationType: MigrationType.FULL_LOAD_AND_CDC,\n tableMappings: new TableMappings().includeSchema('public').toJson(),\n});\n```\n\n---\n\n## Using existing endpoints\n\nBring your own endpoints if they already exist (e.g., created outside CDK or in a different stack):\n\n```typescript\nimport { IDmsEndpoint } from 'cdk-dms-replication';\n\n// Reference an existing endpoint by ARN\nconst existingSource: IDmsEndpoint = {\n endpointArn: 'arn:aws:dms:us-east-1:123456789012:endpoint:ABCDEF',\n};\n\nnew DmsMigrationPipeline(stack, 'Pipeline', {\n vpc,\n migrationType: MigrationType.CDC,\n existingSourceEndpoint: existingSource,\n targetEndpoint: {\n engine: EndpointEngine.S3,\n s3Settings: { ... },\n },\n});\n```\n\n---\n\n## Secrets Manager integration\n\nFor production workloads, store credentials in AWS Secrets Manager and let DMS retrieve them directly (no plaintext in CloudFormation):\n\n```typescript\nsourceEndpoint: {\n engine: EndpointEngine.MYSQL,\n serverName: 'mysql.example.com',\n port: 3306,\n mySqlSettings: {\n secretsManagerSecretId: 'arn:aws:secretsmanager:us-east-1:123456789012:secret:dms/mysql-abc123',\n secretsManagerAccessRoleArn: dmsSecretsRole.roleArn,\n },\n},\n```\n\nThe secret must contain `username` and `password` keys. See [Using AWS Secrets Manager to access database credentials](https://docs.aws.amazon.com/dms/latest/userguide/CHAP_Security.html#security-iam-secretsmanager) for the required secret format and IAM permissions.\n\n---\n\n## Cross-account migrations\n\nDMS supports migrating data between AWS accounts. The replication instance always lives in one account (the **DMS account**) and connects to source and target databases over the network, regardless of which account owns them.\n\n### Prerequisites\n\nTwo things must be true before the construct can help:\n\n1. **Network connectivity** — The replication instance's VPC must be able to reach both endpoints. Establish this with [VPC peering](https://docs.aws.amazon.com/vpc/latest/peering/what-is-vpc-peering.html), [AWS Transit Gateway](https://docs.aws.amazon.com/vpc/latest/tgw/what-is-transit-gateway.html), or [AWS PrivateLink](https://docs.aws.amazon.com/vpc/latest/privatelink/what-is-privatelink.html) before deploying. The construct has no visibility into routing — it will synthesise correctly regardless, but the task will fail at runtime if the endpoints are unreachable.\n\n2. **IAM cross-account trust** — For AWS-managed targets (S3, Kinesis, Redshift, DynamoDB, etc.) owned by a different account, DMS needs an IAM role in the **target account** that trusts the DMS service principal in the **DMS account**.\n\n### CDK stack setup\n\nCDK cannot pass constructs like `ec2.IVpc` across account boundaries. Use `Vpc.fromLookup` in the DMS account stack to reference the VPC by ID:\n\n```typescript\n// DMS account stack (e.g. account 111111111111)\nconst vpc = ec2.Vpc.fromLookup(stack, 'Vpc', {\n vpcId: 'vpc-0abc1234def567890',\n});\n```\n\n### Database endpoints (any engine)\n\nFor source or target databases running in another account, provide the hostname that is reachable from the replication instance's VPC (private IP, private DNS name, or VPC peering DNS). No special construct configuration is needed beyond what you would use for a same-account migration.\n\n```typescript\nnew DmsMigrationPipeline(stack, 'Pipeline', {\n vpc,\n migrationType: MigrationType.FULL_LOAD_AND_CDC,\n\n // Source DB in account 222222222222, reachable via Transit Gateway\n sourceEndpoint: {\n engine: EndpointEngine.ORACLE,\n serverName: '10.1.2.3', // private IP from peered VPC\n port: 1521,\n username: 'dms_user',\n password: cdk.SecretValue.secretsManager('oracle-dms-secret'),\n databaseName: 'ORCL',\n },\n\n // Target in the same DMS account — normal config\n targetEndpoint: {\n engine: EndpointEngine.AURORA_POSTGRESQL,\n serverName: cluster.clusterEndpoint.hostname,\n port: 5432,\n username: 'dms_user',\n password: cdk.SecretValue.secretsManager('aurora-dms-secret'),\n databaseName: 'mydb',\n },\n});\n```\n\n### AWS-managed targets in another account (S3, Kinesis, Redshift, etc.)\n\nWhen the target service lives in a different account, create an IAM role in the **target account** that DMS (running in the DMS account) can assume. Pass its ARN via `serviceAccessRoleArn`.\n\n**Step 1 — Create the cross-account role in the target account (222222222222):**\n\n```typescript\n// In a stack deployed to the TARGET account (222222222222)\nconst crossAccountDmsRole = new iam.Role(targetStack, 'DmsCrossAccountRole', {\n assumedBy: new iam.CompositePrincipal(\n // Allow DMS in the DMS account to assume this role\n new iam.ArnPrincipal(`arn:aws:iam::111111111111:role/dms-vpc-role`),\n new iam.ServicePrincipal('dms.amazonaws.com'),\n ),\n inlinePolicies: {\n S3Access: new iam.PolicyDocument({\n statements: [\n new iam.PolicyStatement({\n actions: ['s3:PutObject', 's3:DeleteObject', 's3:ListBucket'],\n resources: [\n targetBucket.bucketArn,\n `${targetBucket.bucketArn}/*`,\n ],\n }),\n ],\n }),\n },\n});\n```\n\n**Step 2 — Reference the role ARN in the DMS account stack:**\n\n```typescript\n// In the DMS account stack (111111111111)\nnew DmsMigrationPipeline(stack, 'Pipeline', {\n vpc,\n migrationType: MigrationType.FULL_LOAD,\n sourceEndpoint: {\n engine: EndpointEngine.MYSQL,\n serverName: 'mysql.internal.example.com',\n port: 3306,\n username: 'dms_user',\n password: cdk.SecretValue.secretsManager('mysql-dms-secret'),\n databaseName: 'orders',\n },\n targetEndpoint: {\n engine: EndpointEngine.S3,\n s3Settings: {\n bucketName: 'target-account-bucket', // bucket in account 222222222222\n serviceAccessRoleArn: 'arn:aws:iam::222222222222:role/DmsCrossAccountRole',\n },\n },\n});\n```\n\nThe same pattern applies to Kinesis, Redshift, DynamoDB, and other AWS-managed targets — create the role in the target account, grant it the permissions that service needs, and pass its ARN to the relevant `serviceAccessRoleArn` field.\n\n### Cross-account Secrets Manager\n\nIf the database credentials are stored in Secrets Manager in the source account (222222222222) but DMS runs in a different account (111111111111):\n\n1. Add a [resource-based policy](https://docs.aws.amazon.com/secretsmanager/latest/userguide/auth-and-access_resource-based-policies.html) to the secret in account 222222222222 that allows the DMS account's role to call `secretsmanager:GetSecretValue`.\n2. Pass the full secret ARN and the cross-account access role ARN to the endpoint settings:\n\n```typescript\nsourceEndpoint: {\n engine: EndpointEngine.MYSQL,\n serverName: 'mysql.internal.example.com',\n port: 3306,\n mySqlSettings: {\n secretsManagerSecretId:\n 'arn:aws:secretsmanager:us-east-1:222222222222:secret:dms/mysql-abc123',\n secretsManagerAccessRoleArn:\n 'arn:aws:iam::111111111111:role/DmsSecretsManagerRole',\n },\n},\n```\n\n### Cross-account KMS encryption\n\nThe construct creates a KMS key in the DMS account for replication instance storage. If you need the replication instance to write to a KMS-encrypted target in another account, bring your own key and add a cross-account statement to its key policy:\n\n```typescript\n// Key in the DMS account (111111111111), with cross-account decrypt permission\nconst encryptionKey = new kms.Key(stack, 'ReplicationKey', {\n enableKeyRotation: true,\n policy: new iam.PolicyDocument({\n statements: [\n // Standard key admin/use permissions ...\n new iam.PolicyStatement({\n principals: [new iam.AccountPrincipal('222222222222')],\n actions: ['kms:Decrypt', 'kms:GenerateDataKey'],\n resources: ['*'],\n }),\n ],\n }),\n});\n\nnew DmsMigrationPipeline(stack, 'Pipeline', {\n vpc,\n encryptionKey,\n migrationType: MigrationType.FULL_LOAD_AND_CDC,\n sourceEndpoint: { ... },\n targetEndpoint: { ... },\n});\n```\n\n### Summary of cross-account responsibilities\n\n| Concern | Who sets it up | How to configure |\n|---------|---------------|-----------------|\n| Network connectivity | You (VPC peering / TGW / PrivateLink) | Prerequisite — no construct prop |\n| Database endpoint hostname | You | `serverName` — use private IP or private DNS |\n| Cross-account service role (S3, Kinesis, etc.) | You (role in target account) | `serviceAccessRoleArn` |\n| Cross-account Secrets Manager | You (resource policy on secret) | `secretsManagerSecretId` + `secretsManagerAccessRoleArn` |\n| Cross-account KMS | You (key policy) | `encryptionKey` (bring your own) |\n| Replication instance, subnet group, task | This construct | Fully managed |\n\n---\n\n## Observability\n\nA CloudWatch Logs log group is created by default. Customise the retention period or disable it:\n\n```typescript\nimport * as logs from 'aws-cdk-lib/aws-logs';\n\nnew DmsMigrationPipeline(stack, 'Pipeline', {\n vpc,\n migrationType: MigrationType.FULL_LOAD_AND_CDC,\n enableCloudWatchLogs: true,\n logRetention: logs.RetentionDays.THREE_MONTHS,\n sourceEndpoint: { ... },\n targetEndpoint: { ... },\n});\n```\n\n---\n\n## API reference\n\nFull API documentation is available in [API.md](https://github.com/kckempf/cdk-dms-replication/blob/main/API.md) and on [Construct Hub](https://constructs.dev/packages/cdk-dms-replication).\n\n---\n\n## Supported source engines\n\n| Engine | `EndpointEngine` value |\n|--------|----------------------|\n| MySQL | `MYSQL` |\n| Amazon Aurora (MySQL) | `AURORA_MYSQL` |\n| PostgreSQL | `POSTGRES` |\n| Amazon Aurora (PostgreSQL) | `AURORA_POSTGRESQL` |\n| Oracle | `ORACLE` |\n| Microsoft SQL Server | `SQLSERVER` |\n| MariaDB | `MARIADB` |\n| SAP ASE (Sybase) | `SAP_ASE` |\n| IBM Db2 LUW | `IBM_DB2` |\n| IBM Db2 for z/OS | `IBM_DB2_ZOS` |\n| MongoDB | `MONGODB` |\n| Amazon DocumentDB | `DOCDB` |\n| Amazon S3 | `S3` |\n\n## Supported target engines\n\nAll source engines above, plus:\n\n| Engine | `EndpointEngine` value |\n|--------|----------------------|\n| Amazon S3 | `S3` |\n| Amazon DynamoDB | `DYNAMODB` |\n| Amazon Redshift | `REDSHIFT` |\n| Amazon Kinesis Data Streams | `KINESIS` |\n| Apache Kafka / Amazon MSK | `KAFKA` |\n| Amazon OpenSearch Service | `OPENSEARCH` |\n| Amazon Neptune | `NEPTUNE` |\n| Amazon ElastiCache for Redis | `REDIS` |\n\n---\n\n## Security considerations\n\n- The replication instance is placed in **private subnets** by default. Set `publiclyAccessible: true` only if required.\n- Storage is encrypted at rest using a **KMS customer-managed key** (auto-created if you don't provide one).\n- **Do not use the `password` prop in production.** The resolved value is written as plaintext into the CloudFormation template and state file. Use Secrets Manager instead: set `secretsManagerSecretId` and `secretsManagerAccessRoleArn` in the engine-specific settings and omit `password` entirely.\n- Grant DMS only the minimum IAM permissions required for each target engine.\n- For CDC with PostgreSQL, the replication user needs the `rds_replication` role (RDS) or `REPLICATION` privilege (self-managed).\n- For CDC with Oracle, supplemental logging must be enabled on the source database.\n- **`dms-vpc-role` and `dms-cloudwatch-logs-role`** are account-level IAM roles required by DMS. When `createDmsServiceRoles` is `true` (the default), they are created idempotently via a custom resource — if they already exist in the account (created by another stack or manually), the existing roles are silently reused and their trust policies corrected if necessary. The roles are created with `RemovalPolicy.RETAIN` and are **not** lifecycle-managed by the deploying stack; they will not be deleted when the stack is destroyed. If you require the roles to be lifecycle-managed, create them in a dedicated stack and set `createDmsServiceRoles: false`.\n\n---\n\n## Contributing\n\nBug reports and pull requests are welcome. Please open an issue at [github.com/kckempf/cdk-dms-replication/issues](https://github.com/kckempf/cdk-dms-replication/issues) before starting significant work.\n\n---\n\n## Author\n\n[Kevin Kempf](https://github.com/kckempf)\n\n---\n\n## License\n\nApache-2.0 — see [LICENSE](https://github.com/kckempf/cdk-dms-replication/blob/main/LICENSE)\n"
3584
3584
  },
3585
3585
  "repository": {
3586
3586
  "type": "git",
@@ -3622,7 +3622,7 @@
3622
3622
  "kind": "interface",
3623
3623
  "locationInModule": {
3624
3624
  "filename": "src/table-mappings.ts",
3625
- "line": 71
3625
+ "line": 78
3626
3626
  },
3627
3627
  "name": "AddColumnDefinition",
3628
3628
  "properties": [
@@ -3635,7 +3635,7 @@
3635
3635
  "immutable": true,
3636
3636
  "locationInModule": {
3637
3637
  "filename": "src/table-mappings.ts",
3638
- "line": 73
3638
+ "line": 80
3639
3639
  },
3640
3640
  "name": "columnName",
3641
3641
  "type": {
@@ -3651,7 +3651,7 @@
3651
3651
  "immutable": true,
3652
3652
  "locationInModule": {
3653
3653
  "filename": "src/table-mappings.ts",
3654
- "line": 75
3654
+ "line": 82
3655
3655
  },
3656
3656
  "name": "columnType",
3657
3657
  "type": {
@@ -3667,7 +3667,7 @@
3667
3667
  "immutable": true,
3668
3668
  "locationInModule": {
3669
3669
  "filename": "src/table-mappings.ts",
3670
- "line": 77
3670
+ "line": 84
3671
3671
  },
3672
3672
  "name": "columnLength",
3673
3673
  "optional": true,
@@ -3684,7 +3684,7 @@
3684
3684
  "immutable": true,
3685
3685
  "locationInModule": {
3686
3686
  "filename": "src/table-mappings.ts",
3687
- "line": 79
3687
+ "line": 86
3688
3688
  },
3689
3689
  "name": "columnPrecision",
3690
3690
  "optional": true,
@@ -3701,7 +3701,7 @@
3701
3701
  "immutable": true,
3702
3702
  "locationInModule": {
3703
3703
  "filename": "src/table-mappings.ts",
3704
- "line": 81
3704
+ "line": 88
3705
3705
  },
3706
3706
  "name": "columnScale",
3707
3707
  "optional": true,
@@ -3719,7 +3719,7 @@
3719
3719
  "immutable": true,
3720
3720
  "locationInModule": {
3721
3721
  "filename": "src/table-mappings.ts",
3722
- "line": 89
3722
+ "line": 96
3723
3723
  },
3724
3724
  "name": "columnValue",
3725
3725
  "optional": true,
@@ -3736,7 +3736,7 @@
3736
3736
  "immutable": true,
3737
3737
  "locationInModule": {
3738
3738
  "filename": "src/table-mappings.ts",
3739
- "line": 95
3739
+ "line": 102
3740
3740
  },
3741
3741
  "name": "expression",
3742
3742
  "optional": true,
@@ -3884,7 +3884,7 @@
3884
3884
  "kind": "enum",
3885
3885
  "locationInModule": {
3886
3886
  "filename": "src/table-mappings.ts",
3887
- "line": 54
3887
+ "line": 61
3888
3888
  },
3889
3889
  "members": [
3890
3890
  {
@@ -6563,6 +6563,329 @@
6563
6563
  ],
6564
6564
  "symbolId": "src/serverless-pipeline:DmsServerlessPipelineProps"
6565
6565
  },
6566
+ "cdk-dms-replication.DynamoDbAttributeMapping": {
6567
+ "assembly": "cdk-dms-replication",
6568
+ "datatype": true,
6569
+ "docs": {
6570
+ "remarks": "Exactly one of `sourceColumn` or `value` must be set.\nSource columns not listed in `attributeMappings` or `excludeColumns` pass\nthrough with the source column name.",
6571
+ "stability": "stable",
6572
+ "summary": "Mapping from a source column (or DMS expression) to a non-key attribute on the DynamoDB target."
6573
+ },
6574
+ "fqn": "cdk-dms-replication.DynamoDbAttributeMapping",
6575
+ "kind": "interface",
6576
+ "locationInModule": {
6577
+ "filename": "src/table-mappings.ts",
6578
+ "line": 140
6579
+ },
6580
+ "name": "DynamoDbAttributeMapping",
6581
+ "properties": [
6582
+ {
6583
+ "abstract": true,
6584
+ "docs": {
6585
+ "stability": "stable",
6586
+ "summary": "DynamoDB attribute sub-type (`string`, `number`, or `binary`)."
6587
+ },
6588
+ "immutable": true,
6589
+ "locationInModule": {
6590
+ "filename": "src/table-mappings.ts",
6591
+ "line": 155
6592
+ },
6593
+ "name": "attributeSubType",
6594
+ "type": {
6595
+ "fqn": "cdk-dms-replication.DynamoDbAttributeSubType"
6596
+ }
6597
+ },
6598
+ {
6599
+ "abstract": true,
6600
+ "docs": {
6601
+ "stability": "stable",
6602
+ "summary": "Name of the attribute on the DynamoDB target item."
6603
+ },
6604
+ "immutable": true,
6605
+ "locationInModule": {
6606
+ "filename": "src/table-mappings.ts",
6607
+ "line": 153
6608
+ },
6609
+ "name": "targetAttributeName",
6610
+ "type": {
6611
+ "primitive": "string"
6612
+ }
6613
+ },
6614
+ {
6615
+ "abstract": true,
6616
+ "docs": {
6617
+ "remarks": "The builder wraps this\nin the DMS expression `${sourceColumn}`. Mutually exclusive with `value`.",
6618
+ "stability": "stable",
6619
+ "summary": "Name of a single column on the relational source."
6620
+ },
6621
+ "immutable": true,
6622
+ "locationInModule": {
6623
+ "filename": "src/table-mappings.ts",
6624
+ "line": 145
6625
+ },
6626
+ "name": "sourceColumn",
6627
+ "optional": true,
6628
+ "type": {
6629
+ "primitive": "string"
6630
+ }
6631
+ },
6632
+ {
6633
+ "abstract": true,
6634
+ "docs": {
6635
+ "remarks": "Use this for composite values or\nother expressions like `'STATUS#${status}'`. Mutually exclusive with\n`sourceColumn`.",
6636
+ "stability": "stable",
6637
+ "summary": "Raw DMS value expression, used verbatim."
6638
+ },
6639
+ "immutable": true,
6640
+ "locationInModule": {
6641
+ "filename": "src/table-mappings.ts",
6642
+ "line": 151
6643
+ },
6644
+ "name": "value",
6645
+ "optional": true,
6646
+ "type": {
6647
+ "primitive": "string"
6648
+ }
6649
+ }
6650
+ ],
6651
+ "symbolId": "src/table-mappings:DynamoDbAttributeMapping"
6652
+ },
6653
+ "cdk-dms-replication.DynamoDbAttributeSubType": {
6654
+ "assembly": "cdk-dms-replication",
6655
+ "docs": {
6656
+ "stability": "stable",
6657
+ "summary": "DynamoDB attribute sub-type for object-mapping rules."
6658
+ },
6659
+ "fqn": "cdk-dms-replication.DynamoDbAttributeSubType",
6660
+ "kind": "enum",
6661
+ "locationInModule": {
6662
+ "filename": "src/table-mappings.ts",
6663
+ "line": 54
6664
+ },
6665
+ "members": [
6666
+ {
6667
+ "docs": {
6668
+ "stability": "stable"
6669
+ },
6670
+ "name": "STRING"
6671
+ },
6672
+ {
6673
+ "docs": {
6674
+ "stability": "stable"
6675
+ },
6676
+ "name": "NUMBER"
6677
+ },
6678
+ {
6679
+ "docs": {
6680
+ "stability": "stable"
6681
+ },
6682
+ "name": "BINARY"
6683
+ }
6684
+ ],
6685
+ "name": "DynamoDbAttributeSubType",
6686
+ "symbolId": "src/table-mappings:DynamoDbAttributeSubType"
6687
+ },
6688
+ "cdk-dms-replication.DynamoDbKeyMapping": {
6689
+ "assembly": "cdk-dms-replication",
6690
+ "datatype": true,
6691
+ "docs": {
6692
+ "remarks": "Exactly one of `sourceColumn` or `value` must be set.",
6693
+ "stability": "stable",
6694
+ "summary": "Mapping from a source column (or DMS expression) to a DynamoDB partition or sort key attribute."
6695
+ },
6696
+ "fqn": "cdk-dms-replication.DynamoDbKeyMapping",
6697
+ "kind": "interface",
6698
+ "locationInModule": {
6699
+ "filename": "src/table-mappings.ts",
6700
+ "line": 116
6701
+ },
6702
+ "name": "DynamoDbKeyMapping",
6703
+ "properties": [
6704
+ {
6705
+ "abstract": true,
6706
+ "docs": {
6707
+ "stability": "stable",
6708
+ "summary": "DynamoDB attribute sub-type (`string`, `number`, or `binary`)."
6709
+ },
6710
+ "immutable": true,
6711
+ "locationInModule": {
6712
+ "filename": "src/table-mappings.ts",
6713
+ "line": 131
6714
+ },
6715
+ "name": "attributeSubType",
6716
+ "type": {
6717
+ "fqn": "cdk-dms-replication.DynamoDbAttributeSubType"
6718
+ }
6719
+ },
6720
+ {
6721
+ "abstract": true,
6722
+ "docs": {
6723
+ "stability": "stable",
6724
+ "summary": "Name of the partition/sort key attribute on the DynamoDB target item."
6725
+ },
6726
+ "immutable": true,
6727
+ "locationInModule": {
6728
+ "filename": "src/table-mappings.ts",
6729
+ "line": 129
6730
+ },
6731
+ "name": "targetAttributeName",
6732
+ "type": {
6733
+ "primitive": "string"
6734
+ }
6735
+ },
6736
+ {
6737
+ "abstract": true,
6738
+ "docs": {
6739
+ "remarks": "The builder wraps this\nin the DMS expression `${sourceColumn}`. Mutually exclusive with `value`.",
6740
+ "stability": "stable",
6741
+ "summary": "Name of a single column on the relational source."
6742
+ },
6743
+ "immutable": true,
6744
+ "locationInModule": {
6745
+ "filename": "src/table-mappings.ts",
6746
+ "line": 121
6747
+ },
6748
+ "name": "sourceColumn",
6749
+ "optional": true,
6750
+ "type": {
6751
+ "primitive": "string"
6752
+ }
6753
+ },
6754
+ {
6755
+ "abstract": true,
6756
+ "docs": {
6757
+ "remarks": "Use this for composite keys or\nother expressions like `'CUSTOMER#${customer_id}'`. Mutually exclusive\nwith `sourceColumn`.",
6758
+ "stability": "stable",
6759
+ "summary": "Raw DMS value expression, used verbatim."
6760
+ },
6761
+ "immutable": true,
6762
+ "locationInModule": {
6763
+ "filename": "src/table-mappings.ts",
6764
+ "line": 127
6765
+ },
6766
+ "name": "value",
6767
+ "optional": true,
6768
+ "type": {
6769
+ "primitive": "string"
6770
+ }
6771
+ }
6772
+ ],
6773
+ "symbolId": "src/table-mappings:DynamoDbKeyMapping"
6774
+ },
6775
+ "cdk-dms-replication.DynamoDbObjectMappingOptions": {
6776
+ "assembly": "cdk-dms-replication",
6777
+ "datatype": true,
6778
+ "docs": {
6779
+ "stability": "stable",
6780
+ "summary": "Options for `TableMappings.mapToDynamoDb`."
6781
+ },
6782
+ "fqn": "cdk-dms-replication.DynamoDbObjectMappingOptions",
6783
+ "kind": "interface",
6784
+ "locationInModule": {
6785
+ "filename": "src/table-mappings.ts",
6786
+ "line": 159
6787
+ },
6788
+ "name": "DynamoDbObjectMappingOptions",
6789
+ "properties": [
6790
+ {
6791
+ "abstract": true,
6792
+ "docs": {
6793
+ "remarks": "Required.",
6794
+ "stability": "stable",
6795
+ "summary": "Mapping for the DynamoDB partition (hash) key."
6796
+ },
6797
+ "immutable": true,
6798
+ "locationInModule": {
6799
+ "filename": "src/table-mappings.ts",
6800
+ "line": 163
6801
+ },
6802
+ "name": "partitionKey",
6803
+ "type": {
6804
+ "fqn": "cdk-dms-replication.DynamoDbKeyMapping"
6805
+ }
6806
+ },
6807
+ {
6808
+ "abstract": true,
6809
+ "docs": {
6810
+ "stability": "stable",
6811
+ "summary": "Name of the target DynamoDB table."
6812
+ },
6813
+ "immutable": true,
6814
+ "locationInModule": {
6815
+ "filename": "src/table-mappings.ts",
6816
+ "line": 161
6817
+ },
6818
+ "name": "targetTableName",
6819
+ "type": {
6820
+ "primitive": "string"
6821
+ }
6822
+ },
6823
+ {
6824
+ "abstract": true,
6825
+ "docs": {
6826
+ "remarks": "Use this to rename non-key\ncolumns or change their DynamoDB sub-type. Columns not listed here pass\nthrough with the source column name.",
6827
+ "stability": "stable",
6828
+ "summary": "Additional column-to-attribute mappings."
6829
+ },
6830
+ "immutable": true,
6831
+ "locationInModule": {
6832
+ "filename": "src/table-mappings.ts",
6833
+ "line": 173
6834
+ },
6835
+ "name": "attributeMappings",
6836
+ "optional": true,
6837
+ "type": {
6838
+ "collection": {
6839
+ "elementtype": {
6840
+ "fqn": "cdk-dms-replication.DynamoDbAttributeMapping"
6841
+ },
6842
+ "kind": "array"
6843
+ }
6844
+ }
6845
+ },
6846
+ {
6847
+ "abstract": true,
6848
+ "docs": {
6849
+ "stability": "stable",
6850
+ "summary": "Source columns to exclude from the migrated item."
6851
+ },
6852
+ "immutable": true,
6853
+ "locationInModule": {
6854
+ "filename": "src/table-mappings.ts",
6855
+ "line": 167
6856
+ },
6857
+ "name": "excludeColumns",
6858
+ "optional": true,
6859
+ "type": {
6860
+ "collection": {
6861
+ "elementtype": {
6862
+ "primitive": "string"
6863
+ },
6864
+ "kind": "array"
6865
+ }
6866
+ }
6867
+ },
6868
+ {
6869
+ "abstract": true,
6870
+ "docs": {
6871
+ "remarks": "Optional.",
6872
+ "stability": "stable",
6873
+ "summary": "Mapping for the DynamoDB sort (range) key."
6874
+ },
6875
+ "immutable": true,
6876
+ "locationInModule": {
6877
+ "filename": "src/table-mappings.ts",
6878
+ "line": 165
6879
+ },
6880
+ "name": "sortKey",
6881
+ "optional": true,
6882
+ "type": {
6883
+ "fqn": "cdk-dms-replication.DynamoDbKeyMapping"
6884
+ }
6885
+ }
6886
+ ],
6887
+ "symbolId": "src/table-mappings:DynamoDbObjectMappingOptions"
6888
+ },
6566
6889
  "cdk-dms-replication.DynamoDbSettings": {
6567
6890
  "assembly": "cdk-dms-replication",
6568
6891
  "datatype": true,
@@ -10518,7 +10841,7 @@
10518
10841
  "kind": "interface",
10519
10842
  "locationInModule": {
10520
10843
  "filename": "src/table-mappings.ts",
10521
- "line": 99
10844
+ "line": 106
10522
10845
  },
10523
10846
  "name": "RuleObjectLocatorValue",
10524
10847
  "properties": [
@@ -10530,7 +10853,7 @@
10530
10853
  "immutable": true,
10531
10854
  "locationInModule": {
10532
10855
  "filename": "src/table-mappings.ts",
10533
- "line": 100
10856
+ "line": 107
10534
10857
  },
10535
10858
  "name": "schemaName",
10536
10859
  "type": {
@@ -10545,7 +10868,7 @@
10545
10868
  "immutable": true,
10546
10869
  "locationInModule": {
10547
10870
  "filename": "src/table-mappings.ts",
10548
- "line": 102
10871
+ "line": 109
10549
10872
  },
10550
10873
  "name": "columnName",
10551
10874
  "optional": true,
@@ -10561,7 +10884,7 @@
10561
10884
  "immutable": true,
10562
10885
  "locationInModule": {
10563
10886
  "filename": "src/table-mappings.ts",
10564
- "line": 101
10887
+ "line": 108
10565
10888
  },
10566
10889
  "name": "tableName",
10567
10890
  "optional": true,
@@ -11843,7 +12166,7 @@
11843
12166
  "kind": "interface",
11844
12167
  "locationInModule": {
11845
12168
  "filename": "src/table-mappings.ts",
11846
- "line": 106
12169
+ "line": 177
11847
12170
  },
11848
12171
  "name": "TableMappingRule",
11849
12172
  "properties": [
@@ -11855,7 +12178,7 @@
11855
12178
  "immutable": true,
11856
12179
  "locationInModule": {
11857
12180
  "filename": "src/table-mappings.ts",
11858
- "line": 110
12181
+ "line": 181
11859
12182
  },
11860
12183
  "name": "objectLocator",
11861
12184
  "type": {
@@ -11870,7 +12193,7 @@
11870
12193
  "immutable": true,
11871
12194
  "locationInModule": {
11872
12195
  "filename": "src/table-mappings.ts",
11873
- "line": 111
12196
+ "line": 182
11874
12197
  },
11875
12198
  "name": "ruleAction",
11876
12199
  "type": {
@@ -11885,7 +12208,7 @@
11885
12208
  "immutable": true,
11886
12209
  "locationInModule": {
11887
12210
  "filename": "src/table-mappings.ts",
11888
- "line": 108
12211
+ "line": 179
11889
12212
  },
11890
12213
  "name": "ruleId",
11891
12214
  "type": {
@@ -11900,7 +12223,7 @@
11900
12223
  "immutable": true,
11901
12224
  "locationInModule": {
11902
12225
  "filename": "src/table-mappings.ts",
11903
- "line": 109
12226
+ "line": 180
11904
12227
  },
11905
12228
  "name": "ruleName",
11906
12229
  "type": {
@@ -11915,7 +12238,7 @@
11915
12238
  "immutable": true,
11916
12239
  "locationInModule": {
11917
12240
  "filename": "src/table-mappings.ts",
11918
- "line": 107
12241
+ "line": 178
11919
12242
  },
11920
12243
  "name": "ruleType",
11921
12244
  "type": {
@@ -11930,7 +12253,7 @@
11930
12253
  "immutable": true,
11931
12254
  "locationInModule": {
11932
12255
  "filename": "src/table-mappings.ts",
11933
- "line": 113
12256
+ "line": 184
11934
12257
  },
11935
12258
  "name": "oldValue",
11936
12259
  "optional": true,
@@ -11946,7 +12269,7 @@
11946
12269
  "immutable": true,
11947
12270
  "locationInModule": {
11948
12271
  "filename": "src/table-mappings.ts",
11949
- "line": 112
12272
+ "line": 183
11950
12273
  },
11951
12274
  "name": "value",
11952
12275
  "optional": true,
@@ -11973,7 +12296,7 @@
11973
12296
  "kind": "class",
11974
12297
  "locationInModule": {
11975
12298
  "filename": "src/table-mappings.ts",
11976
- "line": 132
12299
+ "line": 203
11977
12300
  },
11978
12301
  "methods": [
11979
12302
  {
@@ -11984,7 +12307,7 @@
11984
12307
  },
11985
12308
  "locationInModule": {
11986
12309
  "filename": "src/table-mappings.ts",
11987
- "line": 393
12310
+ "line": 464
11988
12311
  },
11989
12312
  "name": "addColumn",
11990
12313
  "parameters": [
@@ -12020,7 +12343,7 @@
12020
12343
  },
12021
12344
  "locationInModule": {
12022
12345
  "filename": "src/table-mappings.ts",
12023
- "line": 232
12346
+ "line": 303
12024
12347
  },
12025
12348
  "name": "addPrefixToSchema",
12026
12349
  "parameters": [
@@ -12050,7 +12373,7 @@
12050
12373
  },
12051
12374
  "locationInModule": {
12052
12375
  "filename": "src/table-mappings.ts",
12053
- "line": 294
12376
+ "line": 365
12054
12377
  },
12055
12378
  "name": "addPrefixToTable",
12056
12379
  "parameters": [
@@ -12086,7 +12409,7 @@
12086
12409
  },
12087
12410
  "locationInModule": {
12088
12411
  "filename": "src/table-mappings.ts",
12089
- "line": 244
12412
+ "line": 315
12090
12413
  },
12091
12414
  "name": "addSuffixToSchema",
12092
12415
  "parameters": [
@@ -12116,7 +12439,7 @@
12116
12439
  },
12117
12440
  "locationInModule": {
12118
12441
  "filename": "src/table-mappings.ts",
12119
- "line": 306
12442
+ "line": 377
12120
12443
  },
12121
12444
  "name": "addSuffixToTable",
12122
12445
  "parameters": [
@@ -12152,7 +12475,7 @@
12152
12475
  },
12153
12476
  "locationInModule": {
12154
12477
  "filename": "src/table-mappings.ts",
12155
- "line": 149
12478
+ "line": 220
12156
12479
  },
12157
12480
  "name": "excludeSchema",
12158
12481
  "parameters": [
@@ -12176,7 +12499,7 @@
12176
12499
  },
12177
12500
  "locationInModule": {
12178
12501
  "filename": "src/table-mappings.ts",
12179
- "line": 162
12502
+ "line": 233
12180
12503
  },
12181
12504
  "name": "excludeTable",
12182
12505
  "parameters": [
@@ -12207,7 +12530,7 @@
12207
12530
  },
12208
12531
  "locationInModule": {
12209
12532
  "filename": "src/table-mappings.ts",
12210
- "line": 170
12533
+ "line": 241
12211
12534
  },
12212
12535
  "name": "explicitTable",
12213
12536
  "parameters": [
@@ -12238,7 +12561,7 @@
12238
12561
  },
12239
12562
  "locationInModule": {
12240
12563
  "filename": "src/table-mappings.ts",
12241
- "line": 144
12564
+ "line": 215
12242
12565
  },
12243
12566
  "name": "includeSchema",
12244
12567
  "parameters": [
@@ -12263,7 +12586,7 @@
12263
12586
  },
12264
12587
  "locationInModule": {
12265
12588
  "filename": "src/table-mappings.ts",
12266
- "line": 157
12589
+ "line": 228
12267
12590
  },
12268
12591
  "name": "includeTable",
12269
12592
  "parameters": [
@@ -12286,6 +12609,44 @@
12286
12609
  }
12287
12610
  }
12288
12611
  },
12612
+ {
12613
+ "docs": {
12614
+ "example": "const mappings = new TableMappings()\n .includeTable('public', 'orders')\n .mapToDynamoDb('public', 'orders', {\n targetTableName: 'Orders',\n // Composite partition key from a literal prefix + source column:\n partitionKey: {\n value: 'CUSTOMER#${customer_id}',\n targetAttributeName: 'PK',\n attributeSubType: DynamoDbAttributeSubType.STRING,\n },\n // Bare source column for the sort key:\n sortKey: {\n sourceColumn: 'created_at',\n targetAttributeName: 'CreatedAt',\n attributeSubType: DynamoDbAttributeSubType.STRING,\n },\n excludeColumns: ['internal_flag'],\n attributeMappings: [\n {\n sourceColumn: 'customer_id',\n targetAttributeName: 'CustomerId',\n attributeSubType: DynamoDbAttributeSubType.STRING,\n },\n ],\n })\n .toJson();",
12615
+ "remarks": "Emits a DMS `object-mapping` rule with `rule-action: map-record-to-record`.\nDMS requires this rule type when the target endpoint is DynamoDB; the\npartition key (and optional sort key) tell DMS how to build the item key\nfrom source columns.\n\nSource columns not listed in `attributeMappings` or `excludeColumns` are\nmigrated with the source column name as the attribute name.\n\nFor each key or attribute mapping, set either `sourceColumn` (a single\ncolumn wrapped as `${col}`) or `value` (a raw DMS expression, e.g.\n`'CUSTOMER#${customer_id}'` for composite keys). Exactly one is required.\n\nCalling this method twice with the same `schemaName`/`tableName` emits two\nseparate rules; DMS will reject duplicate object-mapping rules at deploy\ntime. Call it once per source table.",
12616
+ "stability": "stable",
12617
+ "summary": "Map a relational source table to a DynamoDB target table."
12618
+ },
12619
+ "locationInModule": {
12620
+ "filename": "src/table-mappings.ts",
12621
+ "line": 556
12622
+ },
12623
+ "name": "mapToDynamoDb",
12624
+ "parameters": [
12625
+ {
12626
+ "name": "schemaName",
12627
+ "type": {
12628
+ "primitive": "string"
12629
+ }
12630
+ },
12631
+ {
12632
+ "name": "tableName",
12633
+ "type": {
12634
+ "primitive": "string"
12635
+ }
12636
+ },
12637
+ {
12638
+ "name": "options",
12639
+ "type": {
12640
+ "fqn": "cdk-dms-replication.DynamoDbObjectMappingOptions"
12641
+ }
12642
+ }
12643
+ ],
12644
+ "returns": {
12645
+ "type": {
12646
+ "fqn": "cdk-dms-replication.TableMappings"
12647
+ }
12648
+ }
12649
+ },
12289
12650
  {
12290
12651
  "docs": {
12291
12652
  "stability": "stable",
@@ -12293,7 +12654,7 @@
12293
12654
  },
12294
12655
  "locationInModule": {
12295
12656
  "filename": "src/table-mappings.ts",
12296
- "line": 369
12657
+ "line": 440
12297
12658
  },
12298
12659
  "name": "removeColumn",
12299
12660
  "parameters": [
@@ -12329,7 +12690,7 @@
12329
12690
  },
12330
12691
  "locationInModule": {
12331
12692
  "filename": "src/table-mappings.ts",
12332
- "line": 322
12693
+ "line": 393
12333
12694
  },
12334
12695
  "name": "renameColumn",
12335
12696
  "parameters": [
@@ -12371,7 +12732,7 @@
12371
12732
  },
12372
12733
  "locationInModule": {
12373
12734
  "filename": "src/table-mappings.ts",
12374
- "line": 198
12735
+ "line": 269
12375
12736
  },
12376
12737
  "name": "renameSchema",
12377
12738
  "parameters": [
@@ -12401,7 +12762,7 @@
12401
12762
  },
12402
12763
  "locationInModule": {
12403
12764
  "filename": "src/table-mappings.ts",
12404
- "line": 260
12765
+ "line": 331
12405
12766
  },
12406
12767
  "name": "renameTable",
12407
12768
  "parameters": [
@@ -12438,7 +12799,7 @@
12438
12799
  },
12439
12800
  "locationInModule": {
12440
12801
  "filename": "src/table-mappings.ts",
12441
- "line": 476
12802
+ "line": 685
12442
12803
  },
12443
12804
  "name": "toJson",
12444
12805
  "returns": {
@@ -12454,7 +12815,7 @@
12454
12815
  },
12455
12816
  "locationInModule": {
12456
12817
  "filename": "src/table-mappings.ts",
12457
- "line": 339
12818
+ "line": 410
12458
12819
  },
12459
12820
  "name": "toLowerCaseColumn",
12460
12821
  "parameters": [
@@ -12490,7 +12851,7 @@
12490
12851
  },
12491
12852
  "locationInModule": {
12492
12853
  "filename": "src/table-mappings.ts",
12493
- "line": 210
12854
+ "line": 281
12494
12855
  },
12495
12856
  "name": "toLowerCaseSchema",
12496
12857
  "parameters": [
@@ -12515,7 +12876,7 @@
12515
12876
  },
12516
12877
  "locationInModule": {
12517
12878
  "filename": "src/table-mappings.ts",
12518
- "line": 272
12879
+ "line": 343
12519
12880
  },
12520
12881
  "name": "toLowerCaseTable",
12521
12882
  "parameters": [
@@ -12545,7 +12906,7 @@
12545
12906
  },
12546
12907
  "locationInModule": {
12547
12908
  "filename": "src/table-mappings.ts",
12548
- "line": 354
12909
+ "line": 425
12549
12910
  },
12550
12911
  "name": "toUpperCaseColumn",
12551
12912
  "parameters": [
@@ -12581,7 +12942,7 @@
12581
12942
  },
12582
12943
  "locationInModule": {
12583
12944
  "filename": "src/table-mappings.ts",
12584
- "line": 221
12945
+ "line": 292
12585
12946
  },
12586
12947
  "name": "toUpperCaseSchema",
12587
12948
  "parameters": [
@@ -12605,7 +12966,7 @@
12605
12966
  },
12606
12967
  "locationInModule": {
12607
12968
  "filename": "src/table-mappings.ts",
12608
- "line": 283
12969
+ "line": 354
12609
12970
  },
12610
12971
  "name": "toUpperCaseTable",
12611
12972
  "parameters": [
@@ -13480,6 +13841,6 @@
13480
13841
  "symbolId": "src/table-mappings:TransformationAction"
13481
13842
  }
13482
13843
  },
13483
- "version": "0.1.12",
13484
- "fingerprint": "j7i4XdI+mKmEi3G1631qNk2r4nGql7DIfbek2SEI/PU="
13844
+ "version": "0.1.13",
13845
+ "fingerprint": "1l2+vGaCmbjjkpoqGmRhWQ6bQeLCsOXcaxNbcgL9qY4="
13485
13846
  }