cdk-dms-replication 0.0.0

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.
@@ -0,0 +1 @@
1
+ {}
@@ -0,0 +1,229 @@
1
+ import * as cdk from 'aws-cdk-lib';
2
+ import * as ec2 from 'aws-cdk-lib/aws-ec2';
3
+ import * as kms from 'aws-cdk-lib/aws-kms';
4
+ import {
5
+ DmsEndpoint,
6
+ DmsMigrationPipeline,
7
+ DmsReplicationInstance,
8
+ DmsServerlessPipeline,
9
+ EndpointEngine,
10
+ EndpointType,
11
+ MigrationType,
12
+ ReplicationInstanceClass,
13
+ TableMappings,
14
+ TaskSettings,
15
+ LobMode,
16
+ ErrorAction,
17
+ } from '../src';
18
+
19
+ const app = new cdk.App();
20
+
21
+ // ---------------------------------------------------------------------------
22
+ // Stack 1: Classic DmsMigrationPipeline — MySQL → Aurora PostgreSQL
23
+ // ---------------------------------------------------------------------------
24
+ const classicStack = new cdk.Stack(app, 'IntegClassicPipeline');
25
+
26
+ const classicVpc = new ec2.Vpc(classicStack, 'Vpc', {
27
+ maxAzs: 2,
28
+ natGateways: 1,
29
+ });
30
+
31
+ new DmsMigrationPipeline(classicStack, 'MySqlToAurora', {
32
+ vpc: classicVpc,
33
+ migrationType: MigrationType.FULL_LOAD_AND_CDC,
34
+ replicationInstanceClass: ReplicationInstanceClass.R6I_LARGE,
35
+ sourceEndpoint: {
36
+ engine: EndpointEngine.MYSQL,
37
+ serverName: 'mysql.example.com',
38
+ port: 3306,
39
+ username: 'dms_user',
40
+ password: cdk.SecretValue.secretsManager('mysql-dms-secret'),
41
+ databaseName: 'mydb',
42
+ },
43
+ targetEndpoint: {
44
+ engine: EndpointEngine.AURORA_POSTGRESQL,
45
+ serverName: 'aurora.cluster.us-east-1.rds.amazonaws.com',
46
+ port: 5432,
47
+ username: 'dms_user',
48
+ password: cdk.SecretValue.secretsManager('aurora-dms-secret'),
49
+ databaseName: 'mydb',
50
+ },
51
+ tableMappings: new TableMappings().includeSchema('public').toJson(),
52
+ taskSettings: new TaskSettings()
53
+ .withLobMode(LobMode.LIMITED_LOB, 32)
54
+ .withFullLoadSubTasks(16)
55
+ .withBatchApply(true, 5, 60)
56
+ .withDataErrorPolicy(ErrorAction.IGNORE_RECORD, 100)
57
+ .toJson(),
58
+ multiAz: true,
59
+ });
60
+
61
+ // ---------------------------------------------------------------------------
62
+ // Stack 2: Classic pipeline — Oracle → S3 (no CDC roles, shared KMS key)
63
+ // ---------------------------------------------------------------------------
64
+ const oracleStack = new cdk.Stack(app, 'IntegOracleToS3');
65
+
66
+ const oracleVpc = new ec2.Vpc(oracleStack, 'Vpc', { maxAzs: 2, natGateways: 1 });
67
+ const sharedKey = new kms.Key(oracleStack, 'SharedKey', { enableKeyRotation: true });
68
+
69
+ new DmsMigrationPipeline(oracleStack, 'OracleToS3', {
70
+ vpc: oracleVpc,
71
+ migrationType: MigrationType.FULL_LOAD,
72
+ replicationInstanceClass: ReplicationInstanceClass.C6I_2XLARGE,
73
+ encryptionKey: sharedKey,
74
+ sourceEndpoint: {
75
+ engine: EndpointEngine.ORACLE,
76
+ serverName: 'oracle.example.com',
77
+ port: 1521,
78
+ username: 'dms_user',
79
+ password: cdk.SecretValue.secretsManager('oracle-dms-secret'),
80
+ databaseName: 'ORCL',
81
+ oracleSettings: {
82
+ addSupplementalLogging: true,
83
+ useLogminerReader: true,
84
+ },
85
+ },
86
+ targetEndpoint: {
87
+ engine: EndpointEngine.S3,
88
+ s3Settings: {
89
+ bucketName: 'my-dms-target-bucket',
90
+ serviceAccessRoleArn: 'arn:aws:iam::123456789012:role/dms-s3-role',
91
+ dataFormat: 'parquet' as any,
92
+ datePartitionEnabled: true,
93
+ },
94
+ },
95
+ createDmsServiceRoles: false,
96
+ });
97
+
98
+ // ---------------------------------------------------------------------------
99
+ // Stack 3: Serverless pipeline — PostgreSQL → Redshift
100
+ // ---------------------------------------------------------------------------
101
+ const serverlessStack = new cdk.Stack(app, 'IntegServerlessPipeline');
102
+
103
+ const serverlessVpc = new ec2.Vpc(serverlessStack, 'Vpc', { maxAzs: 2, natGateways: 1 });
104
+
105
+ new DmsServerlessPipeline(serverlessStack, 'PgToRedshift', {
106
+ vpc: serverlessVpc,
107
+ maxCapacityUnits: 16,
108
+ minCapacityUnits: 2,
109
+ migrationType: MigrationType.FULL_LOAD_AND_CDC,
110
+ sourceEndpoint: {
111
+ engine: EndpointEngine.POSTGRES,
112
+ serverName: 'pg.example.com',
113
+ port: 5432,
114
+ username: 'dms_user',
115
+ password: cdk.SecretValue.secretsManager('pg-dms-secret'),
116
+ databaseName: 'mydb',
117
+ postgreSqlSettings: {
118
+ pluginName: 'pglogical' as any,
119
+ heartbeatEnable: true,
120
+ },
121
+ },
122
+ targetEndpoint: {
123
+ engine: EndpointEngine.REDSHIFT,
124
+ serverName: 'redshift.cluster.us-east-1.redshift.amazonaws.com',
125
+ port: 5439,
126
+ username: 'dms_user',
127
+ password: cdk.SecretValue.secretsManager('redshift-dms-secret'),
128
+ databaseName: 'mydb',
129
+ },
130
+ tableMappings: new TableMappings().includeSchema('public').toJson(),
131
+ });
132
+
133
+ // ---------------------------------------------------------------------------
134
+ // Stack 4: Standalone DmsReplicationInstance (escape-hatch usage)
135
+ // ---------------------------------------------------------------------------
136
+ const instanceStack = new cdk.Stack(app, 'IntegReplicationInstance');
137
+
138
+ const instanceVpc = new ec2.Vpc(instanceStack, 'Vpc', { maxAzs: 2, natGateways: 1 });
139
+
140
+ const ri = new DmsReplicationInstance(instanceStack, 'Instance', {
141
+ vpc: instanceVpc,
142
+ replicationInstanceClass: ReplicationInstanceClass.T3_MEDIUM,
143
+ allocatedStorage: 50,
144
+ engineVersion: '3.5.4',
145
+ });
146
+
147
+ ri.allowInboundFrom(
148
+ ec2.Peer.ipv4('10.0.0.0/8'),
149
+ ec2.Port.tcp(5432),
150
+ 'Allow internal PostgreSQL',
151
+ );
152
+
153
+ // ---------------------------------------------------------------------------
154
+ // Stack 5: CDC-only pipeline — SQL Server → Kinesis
155
+ // Exercises MigrationType.CDC and cdcStartPosition, which take a different
156
+ // code path in DmsReplicationTask than FULL_LOAD or FULL_LOAD_AND_CDC.
157
+ // ---------------------------------------------------------------------------
158
+ const cdcStack = new cdk.Stack(app, 'IntegCdcPipeline');
159
+
160
+ const cdcVpc = new ec2.Vpc(cdcStack, 'Vpc', { maxAzs: 2, natGateways: 1 });
161
+
162
+ new DmsMigrationPipeline(cdcStack, 'SqlServerToKinesis', {
163
+ vpc: cdcVpc,
164
+ migrationType: MigrationType.CDC,
165
+ replicationInstanceClass: ReplicationInstanceClass.R6I_LARGE,
166
+ sourceEndpoint: {
167
+ engine: EndpointEngine.SQLSERVER,
168
+ serverName: 'sqlserver.example.com',
169
+ port: 1433,
170
+ username: 'dms_user',
171
+ password: cdk.SecretValue.secretsManager('sqlserver-dms-secret'),
172
+ databaseName: 'mydb',
173
+ sqlServerSettings: {
174
+ readBackupOnly: true,
175
+ tlogAccessMode: 'BackupOnly',
176
+ },
177
+ },
178
+ targetEndpoint: {
179
+ engine: EndpointEngine.KINESIS,
180
+ kinesisSettings: {
181
+ streamArn: 'arn:aws:kinesis:us-east-1:123456789012:stream/dms-cdc-stream',
182
+ serviceAccessRoleArn: 'arn:aws:iam::123456789012:role/dms-kinesis-role',
183
+ },
184
+ },
185
+ cdcStartPosition: '0000000000000001:00000001:00000001',
186
+ tableMappings: new TableMappings().includeSchema('%').toJson(),
187
+ });
188
+
189
+ // ---------------------------------------------------------------------------
190
+ // Stack 6: Existing endpoints escape hatch — pre-created DmsEndpoint objects
191
+ // Exercises existingSourceEndpoint / existingTargetEndpoint, which skip
192
+ // endpoint creation inside the pipeline construct.
193
+ // ---------------------------------------------------------------------------
194
+ const escapeStack = new cdk.Stack(app, 'IntegExistingEndpoints');
195
+
196
+ const escapeVpc = new ec2.Vpc(escapeStack, 'Vpc', { maxAzs: 2, natGateways: 1 });
197
+
198
+ const existingSource = new DmsEndpoint(escapeStack, 'Source', {
199
+ endpointType: EndpointType.SOURCE,
200
+ engine: EndpointEngine.POSTGRES,
201
+ serverName: 'pg.example.com',
202
+ port: 5432,
203
+ username: 'dms_user',
204
+ password: cdk.SecretValue.secretsManager('pg-dms-secret'),
205
+ databaseName: 'appdb',
206
+ postgreSqlSettings: {
207
+ pluginName: 'pglogical' as any,
208
+ slotName: 'dms_replication_slot',
209
+ },
210
+ });
211
+
212
+ const existingTarget = new DmsEndpoint(escapeStack, 'Target', {
213
+ endpointType: EndpointType.TARGET,
214
+ engine: EndpointEngine.S3,
215
+ s3Settings: {
216
+ bucketName: 'dms-escape-bucket',
217
+ serviceAccessRoleArn: 'arn:aws:iam::123456789012:role/dms-s3-role',
218
+ },
219
+ });
220
+
221
+ new DmsMigrationPipeline(escapeStack, 'Pipeline', {
222
+ vpc: escapeVpc,
223
+ migrationType: MigrationType.FULL_LOAD_AND_CDC,
224
+ existingSourceEndpoint: existingSource,
225
+ existingTargetEndpoint: existingTarget,
226
+ tableMappings: new TableMappings().includeSchema('public').toJson(),
227
+ });
228
+
229
+ app.synth();
@@ -0,0 +1,14 @@
1
+ import * as cdk from 'aws-cdk-lib';
2
+ import * as iam from 'aws-cdk-lib/aws-iam';
3
+ /**
4
+ * Returns the account-level `dms-vpc-role` IAM role, creating it once per stack.
5
+ * DMS requires this exact role name before it can place replication instances or
6
+ * serverless configs inside a VPC.
7
+ */
8
+ export declare function ensureDmsVpcRole(stack: cdk.Stack): iam.Role;
9
+ /**
10
+ * Returns the account-level `dms-cloudwatch-logs-role` IAM role, creating it
11
+ * once per stack. DMS requires this exact role name to publish task logs to
12
+ * CloudWatch Logs.
13
+ */
14
+ export declare function ensureDmsCloudWatchRole(stack: cdk.Stack): iam.Role;
@@ -0,0 +1,42 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.ensureDmsVpcRole = ensureDmsVpcRole;
4
+ exports.ensureDmsCloudWatchRole = ensureDmsCloudWatchRole;
5
+ const iam = require("aws-cdk-lib/aws-iam");
6
+ /**
7
+ * Returns the account-level `dms-vpc-role` IAM role, creating it once per stack.
8
+ * DMS requires this exact role name before it can place replication instances or
9
+ * serverless configs inside a VPC.
10
+ */
11
+ function ensureDmsVpcRole(stack) {
12
+ const existing = stack.node.tryFindChild('DmsVpcRole');
13
+ if (existing)
14
+ return existing;
15
+ return new iam.Role(stack, 'DmsVpcRole', {
16
+ roleName: 'dms-vpc-role',
17
+ assumedBy: new iam.ServicePrincipal('dms.amazonaws.com'),
18
+ managedPolicies: [
19
+ iam.ManagedPolicy.fromAwsManagedPolicyName('service-role/AmazonDMSVPCManagementRole'),
20
+ ],
21
+ description: 'Allows DMS to manage VPC resources for replication instances',
22
+ });
23
+ }
24
+ /**
25
+ * Returns the account-level `dms-cloudwatch-logs-role` IAM role, creating it
26
+ * once per stack. DMS requires this exact role name to publish task logs to
27
+ * CloudWatch Logs.
28
+ */
29
+ function ensureDmsCloudWatchRole(stack) {
30
+ const existing = stack.node.tryFindChild('DmsCloudWatchLogsRole');
31
+ if (existing)
32
+ return existing;
33
+ return new iam.Role(stack, 'DmsCloudWatchLogsRole', {
34
+ roleName: 'dms-cloudwatch-logs-role',
35
+ assumedBy: new iam.ServicePrincipal('dms.amazonaws.com'),
36
+ managedPolicies: [
37
+ iam.ManagedPolicy.fromAwsManagedPolicyName('service-role/AmazonDMSCloudWatchLogsRole'),
38
+ ],
39
+ description: 'Allows DMS to publish task logs to CloudWatch Logs',
40
+ });
41
+ }
42
+ //# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiZG1zLXJvbGVzLmpzIiwic291cmNlUm9vdCI6IiIsInNvdXJjZXMiOlsiLi4vc3JjL2Rtcy1yb2xlcy50cyJdLCJuYW1lcyI6W10sIm1hcHBpbmdzIjoiOztBQVFBLDRDQVdDO0FBT0QsMERBV0M7QUFwQ0QsMkNBQTJDO0FBRTNDOzs7O0dBSUc7QUFDSCxTQUFnQixnQkFBZ0IsQ0FBQyxLQUFnQjtJQUMvQyxNQUFNLFFBQVEsR0FBRyxLQUFLLENBQUMsSUFBSSxDQUFDLFlBQVksQ0FBQyxZQUFZLENBQUMsQ0FBQztJQUN2RCxJQUFJLFFBQVE7UUFBRSxPQUFPLFFBQW9CLENBQUM7SUFDMUMsT0FBTyxJQUFJLEdBQUcsQ0FBQyxJQUFJLENBQUMsS0FBSyxFQUFFLFlBQVksRUFBRTtRQUN2QyxRQUFRLEVBQUUsY0FBYztRQUN4QixTQUFTLEVBQUUsSUFBSSxHQUFHLENBQUMsZ0JBQWdCLENBQUMsbUJBQW1CLENBQUM7UUFDeEQsZUFBZSxFQUFFO1lBQ2YsR0FBRyxDQUFDLGFBQWEsQ0FBQyx3QkFBd0IsQ0FBQyx5Q0FBeUMsQ0FBQztTQUN0RjtRQUNELFdBQVcsRUFBRSw4REFBOEQ7S0FDNUUsQ0FBQyxDQUFDO0FBQ0wsQ0FBQztBQUVEOzs7O0dBSUc7QUFDSCxTQUFnQix1QkFBdUIsQ0FBQyxLQUFnQjtJQUN0RCxNQUFNLFFBQVEsR0FBRyxLQUFLLENBQUMsSUFBSSxDQUFDLFlBQVksQ0FBQyx1QkFBdUIsQ0FBQyxDQUFDO0lBQ2xFLElBQUksUUFBUTtRQUFFLE9BQU8sUUFBb0IsQ0FBQztJQUMxQyxPQUFPLElBQUksR0FBRyxDQUFDLElBQUksQ0FBQyxLQUFLLEVBQUUsdUJBQXVCLEVBQUU7UUFDbEQsUUFBUSxFQUFFLDBCQUEwQjtRQUNwQyxTQUFTLEVBQUUsSUFBSSxHQUFHLENBQUMsZ0JBQWdCLENBQUMsbUJBQW1CLENBQUM7UUFDeEQsZUFBZSxFQUFFO1lBQ2YsR0FBRyxDQUFDLGFBQWEsQ0FBQyx3QkFBd0IsQ0FBQywwQ0FBMEMsQ0FBQztTQUN2RjtRQUNELFdBQVcsRUFBRSxvREFBb0Q7S0FDbEUsQ0FBQyxDQUFDO0FBQ0wsQ0FBQyIsInNvdXJjZXNDb250ZW50IjpbImltcG9ydCAqIGFzIGNkayBmcm9tICdhd3MtY2RrLWxpYic7XG5pbXBvcnQgKiBhcyBpYW0gZnJvbSAnYXdzLWNkay1saWIvYXdzLWlhbSc7XG5cbi8qKlxuICogUmV0dXJucyB0aGUgYWNjb3VudC1sZXZlbCBgZG1zLXZwYy1yb2xlYCBJQU0gcm9sZSwgY3JlYXRpbmcgaXQgb25jZSBwZXIgc3RhY2suXG4gKiBETVMgcmVxdWlyZXMgdGhpcyBleGFjdCByb2xlIG5hbWUgYmVmb3JlIGl0IGNhbiBwbGFjZSByZXBsaWNhdGlvbiBpbnN0YW5jZXMgb3JcbiAqIHNlcnZlcmxlc3MgY29uZmlncyBpbnNpZGUgYSBWUEMuXG4gKi9cbmV4cG9ydCBmdW5jdGlvbiBlbnN1cmVEbXNWcGNSb2xlKHN0YWNrOiBjZGsuU3RhY2spOiBpYW0uUm9sZSB7XG4gIGNvbnN0IGV4aXN0aW5nID0gc3RhY2subm9kZS50cnlGaW5kQ2hpbGQoJ0Rtc1ZwY1JvbGUnKTtcbiAgaWYgKGV4aXN0aW5nKSByZXR1cm4gZXhpc3RpbmcgYXMgaWFtLlJvbGU7XG4gIHJldHVybiBuZXcgaWFtLlJvbGUoc3RhY2ssICdEbXNWcGNSb2xlJywge1xuICAgIHJvbGVOYW1lOiAnZG1zLXZwYy1yb2xlJyxcbiAgICBhc3N1bWVkQnk6IG5ldyBpYW0uU2VydmljZVByaW5jaXBhbCgnZG1zLmFtYXpvbmF3cy5jb20nKSxcbiAgICBtYW5hZ2VkUG9saWNpZXM6IFtcbiAgICAgIGlhbS5NYW5hZ2VkUG9saWN5LmZyb21Bd3NNYW5hZ2VkUG9saWN5TmFtZSgnc2VydmljZS1yb2xlL0FtYXpvbkRNU1ZQQ01hbmFnZW1lbnRSb2xlJyksXG4gICAgXSxcbiAgICBkZXNjcmlwdGlvbjogJ0FsbG93cyBETVMgdG8gbWFuYWdlIFZQQyByZXNvdXJjZXMgZm9yIHJlcGxpY2F0aW9uIGluc3RhbmNlcycsXG4gIH0pO1xufVxuXG4vKipcbiAqIFJldHVybnMgdGhlIGFjY291bnQtbGV2ZWwgYGRtcy1jbG91ZHdhdGNoLWxvZ3Mtcm9sZWAgSUFNIHJvbGUsIGNyZWF0aW5nIGl0XG4gKiBvbmNlIHBlciBzdGFjay4gRE1TIHJlcXVpcmVzIHRoaXMgZXhhY3Qgcm9sZSBuYW1lIHRvIHB1Ymxpc2ggdGFzayBsb2dzIHRvXG4gKiBDbG91ZFdhdGNoIExvZ3MuXG4gKi9cbmV4cG9ydCBmdW5jdGlvbiBlbnN1cmVEbXNDbG91ZFdhdGNoUm9sZShzdGFjazogY2RrLlN0YWNrKTogaWFtLlJvbGUge1xuICBjb25zdCBleGlzdGluZyA9IHN0YWNrLm5vZGUudHJ5RmluZENoaWxkKCdEbXNDbG91ZFdhdGNoTG9nc1JvbGUnKTtcbiAgaWYgKGV4aXN0aW5nKSByZXR1cm4gZXhpc3RpbmcgYXMgaWFtLlJvbGU7XG4gIHJldHVybiBuZXcgaWFtLlJvbGUoc3RhY2ssICdEbXNDbG91ZFdhdGNoTG9nc1JvbGUnLCB7XG4gICAgcm9sZU5hbWU6ICdkbXMtY2xvdWR3YXRjaC1sb2dzLXJvbGUnLFxuICAgIGFzc3VtZWRCeTogbmV3IGlhbS5TZXJ2aWNlUHJpbmNpcGFsKCdkbXMuYW1hem9uYXdzLmNvbScpLFxuICAgIG1hbmFnZWRQb2xpY2llczogW1xuICAgICAgaWFtLk1hbmFnZWRQb2xpY3kuZnJvbUF3c01hbmFnZWRQb2xpY3lOYW1lKCdzZXJ2aWNlLXJvbGUvQW1hem9uRE1TQ2xvdWRXYXRjaExvZ3NSb2xlJyksXG4gICAgXSxcbiAgICBkZXNjcmlwdGlvbjogJ0FsbG93cyBETVMgdG8gcHVibGlzaCB0YXNrIGxvZ3MgdG8gQ2xvdWRXYXRjaCBMb2dzJyxcbiAgfSk7XG59XG4iXX0=