cdk-rds-sql 8.1.0__py3-none-any.whl

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,589 @@
1
+ Metadata-Version: 2.1
2
+ Name: cdk-rds-sql
3
+ Version: 8.1.0
4
+ Summary: A CDK construct that allows creating roles or users and databases on Aurora Serverless PostgreSQL or MySQL/MariaDB clusters, as well as AWS DSQL clusters.
5
+ Home-page: https://github.com/berenddeboer/cdk-rds-sql.git
6
+ Author: Berend de Boer<berend@pobox.com>
7
+ License: Apache-2.0
8
+ Project-URL: Source, https://github.com/berenddeboer/cdk-rds-sql.git
9
+ Classifier: Intended Audience :: Developers
10
+ Classifier: Operating System :: OS Independent
11
+ Classifier: Programming Language :: JavaScript
12
+ Classifier: Programming Language :: Python :: 3 :: Only
13
+ Classifier: Programming Language :: Python :: 3.9
14
+ Classifier: Programming Language :: Python :: 3.10
15
+ Classifier: Programming Language :: Python :: 3.11
16
+ Classifier: Typing :: Typed
17
+ Classifier: Development Status :: 5 - Production/Stable
18
+ Classifier: License :: OSI Approved
19
+ Requires-Python: ~=3.9
20
+ Description-Content-Type: text/markdown
21
+ License-File: LICENSE
22
+ Requires-Dist: aws-cdk-lib <3.0.0,>=2.214.0
23
+ Requires-Dist: constructs <11.0.0,>=10.4.2
24
+ Requires-Dist: jsii <2.0.0,>=1.129.0
25
+ Requires-Dist: publication >=0.0.3
26
+ Requires-Dist: typeguard ==2.13.3
27
+
28
+ # About
29
+
30
+ This CDK construct library makes it possible to create databases,
31
+ schemas, and roles in an Aurora Serverless v2, RDS Database Cluster,
32
+ Database Instance, or AWS DSQL clusters. PostgreSQL, MySQL, and DSQL
33
+ databases are supported.
34
+
35
+ This construct library is intended to be used in enterprise
36
+ environments, and works in isolated subnets.
37
+
38
+ <p align="left">
39
+ <a href="https://github.com/semantic-release/semantic-release"><img src="https://img.shields.io/badge/semantic--release-angular-e10079?logo=semantic-release" alt=semantic-release: angular"></a>
40
+ <a href="https://github.com/berenddeboer/cdk-rds-sql/actions/workflows/release.yml"><img src="https://github.com/berenddeboer/cdk-rds-sql/actions/workflows/release.yml/badge.svg" alt="Release badge"></a>
41
+ </p>
42
+
43
+ # Requirements
44
+
45
+ * CDK v2.
46
+
47
+ # Installation
48
+
49
+ ```
50
+ npm i cdk-rds-sql
51
+ ```
52
+
53
+ # Usage
54
+
55
+ ## Provider
56
+
57
+ First setup your VPC and create your cluster:
58
+
59
+ ```python
60
+ import { Duration, RemovalPolicy } from "aws-cdk-lib"
61
+ import * as ec2 from "aws-cdk-lib/aws-ec2"
62
+ import * as rds from "aws-cdk-lib/aws-rds"
63
+
64
+ const vpc = ec2.Vpc.fromLookup(this, "Vpc", {
65
+ vpcId: this.node.tryGetContext("vpc-id"),
66
+ })
67
+
68
+ const cluster = new rds.ServerlessCluster(this, "Cluster", {
69
+ vpc: vpc,
70
+ vpcSubnets: {
71
+ subnetType: ec2.SubnetType.PRIVATE_ISOLATED,
72
+ },
73
+ engine: rds.DatabaseClusterEngine.AURORA_POSTGRESQL,
74
+ parameterGroup: rds.ParameterGroup.fromParameterGroupName(
75
+ this,
76
+ "ParameterGroup",
77
+ "default.aurora-postgresql11"
78
+ ),
79
+ removalPolicy: RemovalPolicy.DESTROY,
80
+ scaling: {
81
+ autoPause: Duration.minutes(60),
82
+ minCapacity: rds.AuroraCapacityUnit.ACU_2,
83
+ maxCapacity: rds.AuroraCapacityUnit.ACU_2,
84
+ },
85
+ })
86
+ ```
87
+
88
+ Then create a provider which will connect to your database. For a cluster:
89
+
90
+ ```python
91
+ import { Provider } from "cdk-rds-sql"
92
+
93
+ const provider = new Provider(this, "Provider", {
94
+ vpc: vpc,
95
+ cluster: cluster,
96
+ secret: cluster.secret!,
97
+ })
98
+ ```
99
+
100
+ For an instance:
101
+
102
+ ```python
103
+ import { Provider } from "cdk-rds-sql"
104
+
105
+ const provider = new Provider(this, "Provider", {
106
+ vpc: vpc,
107
+ instance: instance,
108
+ secret: cluster.secret!,
109
+ })
110
+ ```
111
+
112
+ The provider will setup a lambda, which normally lives in the same VPC
113
+ as the database. You can give a different VPC, as long as that VPC has
114
+ access to the VPC of the database. Only the provider lambda will talk
115
+ to your database.
116
+
117
+ The provider will by default use the private isolated subnet of the
118
+ VPC. Your isolated network must have a VPC endpoint to AWS Secrets Manager
119
+ and possibly KMS as well. If you want to use a subnet with egress
120
+ access in case you have no such VPC endpoints, specify the subnet as
121
+ follows:
122
+
123
+ ```python
124
+ import { Provider } from "cdk-rds-sql"
125
+
126
+ const provider = new Provider(this, "Provider", {
127
+ vpc: vpc,
128
+ vpcSubnets: {
129
+ subnetType: ec2.SubnetType.PRIVATE_WITH_EGRESS,
130
+ },
131
+ cluster: cluster,
132
+ secret: cluster.secret!,
133
+ })
134
+ ```
135
+
136
+ ## Roles
137
+
138
+ Create a postgres role (or mysql user) for a cluster as follows:
139
+
140
+ ```python
141
+ import { Role } from "cdk-rds-sql"
142
+
143
+ const role = new Role(this, "Role", {
144
+ provider: provider,
145
+ roleName: "myrole",
146
+ databaseName: "mydb",
147
+ })
148
+ ```
149
+
150
+ This will automatically create a secret just like `ServerlessCluster`
151
+ does, with all the connection info needed for this user. It's secret
152
+ value is a JSON like:
153
+
154
+ ```json
155
+ {
156
+ "dbClusterIdentifier": "teststack-clustereb1186t9-sh4wpqfdyfvu",
157
+ "password": "very-long-and-boring",
158
+ "dbname": "mydb",
159
+ "engine": "postgres",
160
+ "port": 5432,
161
+ "host": "teststack-clustereb1186t9-sh4wpqfdyfvu.cluster-cgudolabssna.us-east-1.rds.amazonaws.com",
162
+ "username": "myrole"
163
+ }
164
+ ```
165
+
166
+ You can access the secret via `role.secret`.
167
+
168
+ Instead of `databaseName` you can also specify `database` to reference
169
+ an existing database. The role will not be created until the database
170
+ has been created.
171
+
172
+ If you want to make the role the owner of a new database, just specify
173
+ the `databaseName` here, and create the database later.
174
+
175
+ ### IAM Authentication
176
+
177
+ Instead of password-based authentication, you can create roles that
178
+ use AWS IAM database authentication. This eliminates the need to store
179
+ database passwords and provides enhanced security through AWS IAM
180
+ policies.
181
+
182
+ ```python
183
+ import { Role } from "cdk-rds-sql"
184
+
185
+ const iamRole = new Role(this, "IamRole", {
186
+ provider: provider,
187
+ roleName: "myiamrole",
188
+ databaseName: "mydb",
189
+ enableIamAuth: true,
190
+ })
191
+ ```
192
+
193
+ When `enableIamAuth` is set to `true`:
194
+
195
+ * **No secret is created** - the `role.secret` property will be `undefined`
196
+ * The database role is created without a password
197
+ * Your application must generate an IAM authentication token at runtime
198
+
199
+ This is the same behavior as DSQL clusters, which always use IAM authentication.
200
+
201
+ **Requirements for IAM Authentication:**
202
+
203
+ * SSL connections are required (enabled by default in this library)
204
+ * Your application must have IAM permissions to connect to the database
205
+ * The database user/role name must match the IAM identity
206
+
207
+ **IAM Policy Example:**
208
+
209
+ Your application will need an IAM policy like this to connect:
210
+
211
+ ```json
212
+ {
213
+ "Version": "2012-10-17",
214
+ "Statement": [
215
+ {
216
+ "Effect": "Allow",
217
+ "Action": ["rds-db:connect"],
218
+ "Resource": [
219
+ "arn:aws:rds-db:region:account-id:dbuser:cluster-resource-id/myiamrole"
220
+ ]
221
+ }
222
+ ]
223
+ }
224
+ ```
225
+
226
+ **Generating an IAM Authentication Token:**
227
+
228
+ At runtime, your application needs to generate an authentication token
229
+ using the AWS SDK. Here's an example using the AWS SDK for JavaScript:
230
+
231
+ ```python
232
+ import { Signer } from "@aws-sdk/rds-signer"
233
+ import { Client } from "pg"
234
+
235
+ const signer = new Signer({
236
+ hostname: "your-cluster.cluster-xxxxx.us-east-1.rds.amazonaws.com",
237
+ port: 5432,
238
+ username: "myiamrole",
239
+ region: "us-east-1",
240
+ })
241
+
242
+ const token = await signer.getAuthToken()
243
+
244
+ const client = new Client({
245
+ host: "your-cluster.cluster-xxxxx.us-east-1.rds.amazonaws.com",
246
+ port: 5432,
247
+ user: "myiamrole",
248
+ database: "mydb",
249
+ password: token,
250
+ ssl: { rejectUnauthorized: true },
251
+ })
252
+
253
+ await client.connect()
254
+ ```
255
+
256
+ Both PostgreSQL and MySQL databases support IAM authentication. For more details, see the [AWS RDS IAM Database Authentication documentation](https://docs.aws.amazon.com/AmazonRDS/latest/UserGuide/UsingWithRDS.IAMDBAuth.html).
257
+
258
+ ### MySQL support
259
+
260
+ In MySQL users are created with '%' as value for the host. It is hard
261
+ to do this better:
262
+
263
+ * Determine the CIDR blocks used by a VPC is not trivial.
264
+ * For imported VPCs you cannot specify the IPv6 CIDR.
265
+ * CIDRs might change without the system knowing, meaning applications
266
+ could lose access to the database at random times, such as when a
267
+ container is restarted.
268
+
269
+ ## Database
270
+
271
+ Create a database as follows:
272
+
273
+ ```python
274
+ import { Database } from "cdk-rds-sql"
275
+
276
+ const database = new Database(this, "Database", {
277
+ provider: provider,
278
+ databaseName: "mydb",
279
+ })
280
+ ```
281
+
282
+ You can provide an owner, which makes it easy to create database owned
283
+ by a new user:
284
+
285
+ ```python
286
+ const database = new Database(this, "Database", {
287
+ provider: provider,
288
+ databaseName: "mydb",
289
+ owner: role,
290
+ })
291
+ ```
292
+
293
+ ## Schema
294
+
295
+ Create a schema in the default database as follows:
296
+
297
+ ```python
298
+ import { Schema } from "cdk-rds-sql"
299
+
300
+ new Schema(this, "Schema", {
301
+ provider: provider,
302
+ schemaName: "myschema",
303
+ })
304
+ ```
305
+
306
+ Or in another database:
307
+
308
+ ```python
309
+ const database = new Database(this, "Database", {
310
+ provider: provider,
311
+ databaseName: "mydb",
312
+ })
313
+
314
+ new Schema(this, "Schema", {
315
+ provider: provider,
316
+ schemaName: "myschema",
317
+ databaseName: database.databaseName,
318
+ })
319
+ ```
320
+
321
+ One may need a role permitted for using schema:
322
+
323
+ ```python
324
+ new Schema(this, "Schema", {
325
+ provider: provider,
326
+ schemaName: "myschema",
327
+ databaseName: database.databaseName,
328
+ role: role,
329
+ })
330
+ ```
331
+
332
+ ## Sql
333
+
334
+ You can insert arbitrary SQL into your database with the `Sql` construct:
335
+
336
+ ```python
337
+ import { Sql } from "cdk-rds-sql"
338
+
339
+ const sql = new Sql(this, "Sql", {
340
+ provider: provider,
341
+ database: database,
342
+ statement: "create table t (i int)",
343
+ })
344
+ ```
345
+
346
+ Create a table if it does not exist, and grant a role privileges:
347
+
348
+ ```python
349
+ const sql = new Sql(this, "Sql", {
350
+ provider: provider,
351
+ database: database,
352
+ statement: `
353
+ create table if not exists t (i int);
354
+ grant select on t to myrole;
355
+ `,
356
+ })
357
+ ```
358
+
359
+ Rollback sql on stack deletion:
360
+
361
+ ```python
362
+ const sql = new Sql(this, "Sql", {
363
+ provider: provider,
364
+ database: database,
365
+ statement: `
366
+ create table if not exists t (i int);
367
+ grant select on t to myrole;
368
+ `,
369
+ rollback: `
370
+ DO $$BEGIN
371
+ IF EXISTS (select from pg_database WHERE datname = 't') THEN
372
+ IF EXISTS (select from pg_catalog.pg_roles WHERE rolname = 'myrole') THEN
373
+ revoke select t from myrole;
374
+ END IF;
375
+ drop table t;
376
+ END IF;
377
+ END$$;
378
+ `,
379
+ })
380
+ ```
381
+
382
+ Note that there is no synchronisation between various `Sql`
383
+ constructs, in particular the order in your code does not determine
384
+ the order in which your SQL is executed. This happens in parallel,
385
+ unless you specify an explicit dependency via `sql.node.addDepency()`.
386
+
387
+ There are a lot of concerns when using `Sql`:
388
+
389
+ * When you update your Sql, your previous Sql is not "rolled back",
390
+ the new Sql is simply executed again.
391
+ * When you delete your `Sql` construct the rollback is executed if specified
392
+ * When permission are granted via `Sql` they must be removed via
393
+ rollback to succesfully remove the role
394
+ * Currently the `Sql` constructs has less than 5 minutes to execute
395
+ its work.
396
+ * It is unknown how large your SQL can be.
397
+
398
+ # Parameters
399
+
400
+ Some lambda constructs, in particular [Bref](https://bref.sh/), do not
401
+ support secrets out of the box. Use the `parameterPrefix` property to
402
+ create SSM parameters in addition to a secret:
403
+
404
+ ```python
405
+ const role = new Role(this, "Role", {
406
+ provider: provider,
407
+ roleName: "myrole",
408
+ databaseName: "mydb",
409
+ parameterPrefix: "/my-app/",
410
+ })
411
+ ```
412
+
413
+ This will create `/my-app/username`, `/my-app/password` and such.
414
+
415
+ To access parameters you will need IAM permissions such as:
416
+
417
+ ```python
418
+ initialPolicy: [
419
+ new iam.PolicyStatement({
420
+ actions: ["ssm:GetParameter", "ssm:GetParameters"],
421
+ resources: [
422
+ // Grant access to all parameters under the base path
423
+ `arn:aws:ssm:${this.region}:${this.account}:parameter/my-app/*`,
424
+ ],
425
+ effect: iam.Effect.ALLOW,
426
+ })
427
+ ],
428
+ ```
429
+
430
+ Note that your VPC will need an SSM Parameters interface endpoint to support this.
431
+
432
+ # AWS DSQL Support
433
+
434
+ This construct also supports AWS DSQL. DSQL is a great number of
435
+ limitations around it's postgresql support, so there is some learing
436
+ curve. This library supports creating roles, schemas, and executing
437
+ SQL on DSQL clusters. Creating databases is not supported by DSQL.
438
+
439
+ ## DSQL Provider Setup
440
+
441
+ DSQL clusters don't require a VPC or secrets since they use IAM authentication:
442
+
443
+ ```python
444
+ import * as dsql from "aws-cdk-lib/aws-dsql"
445
+ import { Provider } from "cdk-rds-sql"
446
+
447
+ const dsqlCluster = new dsql.CfnCluster(this, "DsqlCluster", {
448
+ deletionProtectionEnabled: false,
449
+ })
450
+
451
+ const provider = new Provider(this, "Provider", {
452
+ cluster: dsqlCluster,
453
+ // No VPC or secret needed for DSQL
454
+ })
455
+ ```
456
+
457
+ You can access DSQL from within a VPC, but cdk-rds-sql has not been
458
+ specifically tested with that scenario.
459
+
460
+ ## DSQL Roles
461
+
462
+ DSQL always uses IAM authentication, so the `enableIamAuth` property is ignored:
463
+
464
+ ```python
465
+ import { Role } from "cdk-rds-sql"
466
+
467
+ const role = new Role(this, "Role", {
468
+ provider: provider,
469
+ roleName: "myrole",
470
+ })
471
+ ```
472
+
473
+ ## Using DSQL
474
+
475
+ Connecting to DSQL requires a couple of things:
476
+
477
+ 1. Your IAM role needs the `dsql:DbConnect` IAM permission to connect to the cluster.
478
+ 2. You probably want a normal postgres role (not "admin") to use from your apps.
479
+ 3. Your IAM role needs to be connected to your postgres role: use the `IamGrant` construct for that:
480
+
481
+ ```python
482
+ new IamGrant(this, "LambdaIamGrant", {
483
+ provider: provider,
484
+ roleName: role.roleName,
485
+ resourceArn: queryLambda.role!.roleArn,
486
+ })
487
+ ```
488
+ 4. From your code use the Dsql signer to create the password:
489
+
490
+ ```python
491
+ import { DsqlSigner } from "@aws-sdk/dsql-signer"
492
+ import { Client } from "pg"
493
+
494
+ const region =
495
+ process.env["AWS_REGION"] || process.env["AWS_DEFAULT_REGION"] || "us-west-2"
496
+
497
+ // Generate DSQL auth token using AWS SDK
498
+ const signer = new DsqlSigner({
499
+ hostname: host,
500
+ region,
501
+ })
502
+
503
+ const authToken = await signer.getDbConnectAuthToken()
504
+
505
+ const client = new Client({
506
+ host,
507
+ port: 5432,
508
+ user: dbUser,
509
+ database: dbName,
510
+ password: authToken,
511
+ ssl: { rejectUnauthorized: false },
512
+ })
513
+
514
+ await client.connect()
515
+ ```
516
+
517
+ # IPv6
518
+
519
+ If you use the provider in an IPv6 subnet you probably need these settings:
520
+
521
+ ```python
522
+ import { Provider } from "cdk-rds-sql"
523
+
524
+ const provider = new Provider(this, "Provider", {
525
+ ...
526
+ functionProps: {
527
+ ipv6AllowedForDualStack: true,
528
+ allowAllIpv6Outbound: true,
529
+ },
530
+ }
531
+ ```
532
+
533
+ # Disabling SSL
534
+
535
+ The default connection to RDS is ssl enabled. You can disable ssl by
536
+ setting the `ssl` option to `false`:
537
+
538
+ ```python
539
+ const provider = new Provider(this, "Provider", {
540
+ vpc: vpc,
541
+ instance: instance,
542
+ secret: cluster.secret!,
543
+ ssl: false, // default is true
544
+ })
545
+ ```
546
+
547
+ # Working on this code
548
+
549
+ This code is managed by
550
+ [projen](https://github.com/projen/projen/blob/main/README.md). In
551
+ addition [pre-commit](https://pre-commit.com/) is used.
552
+
553
+ So after git clone and `pnpm install` you would do:
554
+
555
+ ```
556
+ pre-commit install --install-hooks --hook-type commit-msg --hook-type pre-commit
557
+ ```
558
+
559
+ to install the pre-commit hooks.
560
+
561
+ ## Testing
562
+
563
+ Test code via projen with:
564
+
565
+ ```
566
+ pnpm exec projen test
567
+ ```
568
+
569
+ You can run the PostgreSQL sample stack with:
570
+
571
+ ```
572
+ pnpm exec projen integ:deploy:postgresql:serverless
573
+ ```
574
+
575
+ If you want to use an existing vpc:
576
+
577
+ ```
578
+ pnpm exec cdk deploy --context vpc-id=vpc-0123456789 TestRdsSqlServerlessV2Stack
579
+ ```
580
+
581
+ You can also test DSQL support with:
582
+
583
+ ```
584
+ pnpm exec projen integ:deploy:dsql
585
+ ```
586
+
587
+ # To do
588
+
589
+ * Update role: will not revoke connect to previous database if database name has changed.
@@ -0,0 +1,9 @@
1
+ cdk_rds_sql/__init__.py,sha256=MEa4TqRiAy_6YrHW9mCTFa6jP0BBNbjmdM1iJtUavds,80783
2
+ cdk_rds_sql/py.typed,sha256=AbpHGcgLb-kRsJGnwFEktk7uzpZOCcBY74-YBdrKVGs,1
3
+ cdk_rds_sql/_jsii/__init__.py,sha256=T8nWXRVOa-oYIWCUORxI3ZI9GkYEdI1oaeHv5wvw1Sw,1433
4
+ cdk_rds_sql/_jsii/cdk-rds-sql@8.1.0.jsii.tgz,sha256=d_anCZnCtrcDyq0IJ-ED3yh9DUq92ZJze4ZBKDH0XEM,646542
5
+ cdk_rds_sql-8.1.0.dist-info/LICENSE,sha256=z8d0m5b2O9McPEK1xHG_dWgUBT6EfBDz6wA0F7xSPTA,11358
6
+ cdk_rds_sql-8.1.0.dist-info/METADATA,sha256=YQWRLdmJ3ig5JeYxhgPaS-Rv-m2kUmUF1OatUKKRrGU,14961
7
+ cdk_rds_sql-8.1.0.dist-info/WHEEL,sha256=BNRMDyzLkkcmlv0J8ppDQkk2VED33SesJDynr9ED1gc,91
8
+ cdk_rds_sql-8.1.0.dist-info/top_level.txt,sha256=FVap9ZNnCdWu7Wf-mpGMeR4SJZ_P8h_U_p0i354ElBM,12
9
+ cdk_rds_sql-8.1.0.dist-info/RECORD,,
@@ -0,0 +1,5 @@
1
+ Wheel-Version: 1.0
2
+ Generator: setuptools (75.3.4)
3
+ Root-Is-Purelib: true
4
+ Tag: py3-none-any
5
+
@@ -0,0 +1 @@
1
+ cdk_rds_sql