DSQL
Serverless Postgres on Aurora DSQL. IAM auth over TLS, schemas and roles declared in code, and optional multi-region clusters.
With no arguments you get one cluster in your stack's region, reached over its public endpoint with IAM auth tokens over TLS — there are no database passwords. Declare schemas to have Anvil create tables and roles, then db.grantConnect(fn, ...) so a Lambda can log in as one of those roles.
Minimal
The smallest config that deploys.
import { App } from '@anvil-cloud/sdk';
import * as anvil from '@anvil-cloud/sdk';
export default new App({
run(ctx) {
const db = new anvil.aws.DSQL('db');
ctx.export('endpoint', db.endpoint);
},
});import anvil_cloud as anvil
def infra(ctx: anvil.Context):
db = anvil.aws.DSQL("db")
ctx.export("endpoint", db.endpoint)
anvil.App(run=infra)package main
import (
"github.com/anvil-cloud/anvil/sdk/go/anvil"
anvilaws "github.com/anvil-cloud/anvil/sdk/go/anvil/aws"
)
func main() {
anvil.Run(anvil.AppConfig{
Run: func(ctx *anvil.Context) error {
db, err := anvilaws.NewDSQL(ctx.PulumiCtx(), "db", nil)
if err != nil {
return err
}
ctx.Export("endpoint", db.Endpoint)
return nil
},
})
}Granting access
Declare a Postgres role in schemas, then grant compute connect access as that role. grantConnect takes an Anvil Lambda (api below). For any other compute, such as an ECS task, an EC2 instance or your own IAM role, create a DSQLConnect with its role ARN, as the Go tab does.
const db = new anvil.aws.DSQL('db', {
schemas: [{
name: 'app',
roles: [{ name: 'app_role', grants: ['SELECT', 'INSERT'] }],
}],
});
db.grantConnect(api, { schemas: [{ schema: 'app', roleName: 'app_role' }] });db = anvil.aws.DSQL("db",
schemas=[{
"name": "app",
"roles": [{"name": "app_role", "grants": ["SELECT", "INSERT"]}],
}],
)
db.grant_connect(api, schemas=[{"schema": "app", "role_name": "app_role"}])db, err := anvilaws.NewDSQL(ctx.PulumiCtx(), "db", &anvilaws.DSQLArgs{
Schemas: anvilaws.DSQLSchemaArray{
anvilaws.DSQLSchemaArgs{
Name: pulumi.String("app"),
Roles: anvilaws.DSQLRoleArray{
anvilaws.DSQLRoleArgs{
Name: pulumi.String("app_role"),
Grants: anvilaws.DSQLGrantArray{anvilaws.DSQLGrant("SELECT"), anvilaws.DSQLGrant("INSERT")},
},
},
},
},
})
if err != nil {
return err
}
// Go has no grantConnect yet: create the DSQLConnect it would, for any IAM role.
if _, err := anvilaws.NewDSQLConnect(ctx.PulumiCtx(), "db-api-connect", &anvilaws.DSQLConnectArgs{
ClusterArns: db.ClusterArns,
TargetRoleArn: api.RoleArn,
TargetName: pulumi.String("api"),
DbRoles: pulumi.StringArray{pulumi.String("app_role")},
DeclaredRoles: db.DeclaredRoles,
RolesTableName: db.RolesTableName,
}); err != nil {
return err
}What Anvil creates
- An IAM policy on the compute's role allowing only
dsql:DbConnect, on this cluster (every region's cluster for multi-region). Notdsql:DbConnectAdmin, so it can never log in asadmin. - An
AWS IAM GRANTmapping that role toapp_roleand nothing else. Logging in as any other database role fails. - Inside the database,
app_rolehas only itsgrants, on its own schema (or just thetableScopingtables). It owns no tables, so it can't alter or drop them. - Removing the grant deletes the policy and runs
AWS IAM REVOKE, so access ends on the next deploy. - Each
{ schema, roleName }must be a role declared inschemas, in that schema, or the deploy fails. Roles made by hand in SQL can't be granted, so access is always managed in code.
Good to know
- Connect as the role, not as
admin: generate aDbConnecttoken (notDbConnectAdmin), and use the role name as the user, databasepostgres, port 5432, with TLS. The Full example tab has the connect code in each language. - Anvil follows DSQL's own limits instead of working around them. Where DSQL doesn't support something, the deploy fails with the reason rather than Anvil emulating it. The architecture page lists the limits that apply.
anvil deploywaits for the schema, role and grant changes to reach the database and prints each result. A change DSQL rejects fails the deploy, and a column whose type differs from your code is a warning. Both repeat on every deploy until the code or the database is fixed.- Removing a table, column or index from
schemasdoesn't drop it. Drop it yourself in SQL if you want it gone. Removing a role does drop it, after revoking its privileges and IAM mappings. - Tables are always owned by
admin, so your roles never own anything and can always be dropped. - Encryption at rest is always on with an AWS-owned key at no cost. For your own KMS key, set
hatch.cluster.kmsEncryptionKey. anvil generatewrites typed query helpers from yourschemas— see the Codegen tab.
The Full example tab has a complete programand the runtime code that uses it. Every argument and attribute is on the Reference tab. More on: Change data capture.