---
inclusion: manual
---
ALWAYS prefer scoped database roles over the admin role. The admin role should ONLY be
used for initial cluster setup, creating roles, and granting permissions. Applications and
services MUST connect using scoped-down database roles with dsql:DbConnect.
---
---
Connect as admin (the only time admin should be used):
-- 1. Create scoped database roles
CREATE ROLE app_readonly WITH LOGIN;
CREATE ROLE app_readwrite WITH LOGIN;
CREATE ROLE user_service WITH LOGIN;
-- 2. Map each to an IAM role (each IAM role needs dsql:DbConnect permission)
AWS IAM GRANT app_readonly TO 'arn:aws:iam::*:role/AppReadOnlyRole';
AWS IAM GRANT app_readwrite TO 'arn:aws:iam::*:role/AppReadWriteRole';
AWS IAM GRANT user_service TO 'arn:aws:iam::*:role/UserServiceRole';
-- 3. Create a dedicated schema for sensitive data
CREATE SCHEMA users_schema;
-- 4. Grant scoped permissions
GRANT USAGE ON SCHEMA public TO app_readonly;
GRANT SELECT ON ALL TABLES IN SCHEMA public TO app_readonly;
GRANT USAGE ON SCHEMA public TO app_readwrite;
GRANT SELECT, INSERT, UPDATE, DELETE ON ALL TABLES IN SCHEMA public TO app_readwrite;
GRANT USAGE ON SCHEMA users_schema TO user_service;
GRANT SELECT, INSERT, UPDATE ON ALL TABLES IN SCHEMA users_schema TO user_service;
GRANT CREATE ON SCHEMA users_schema TO user_service;
---
Each scoped database role requires a corresponding IAM role with dsql:DbConnect:
{
"Version": "2012-10-17",
"Statement": [
{
"Effect": "Allow",
"Action": "dsql:DbConnect",
"Resource": "arn:aws:dsql:*:*:cluster/*"
}
]
}
Reserve dsql:DbConnectAdmin strictly for administrative IAM identities:
{
"Version": "2012-10-17",
"Statement": [
{
"Effect": "Allow",
"Action": "dsql:DbConnectAdmin",
"Resource": "arn:aws:dsql:us-east-1:123456789012:cluster/*"
}
]
}
---
-- Sensitive data: dedicated schema
CREATE TABLE users_schema.profiles (
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
tenant_id VARCHAR(255) NOT NULL,
email VARCHAR(255) NOT NULL,
name VARCHAR(255),
phone VARCHAR(50)
);
-- Non-sensitive data: public schema
CREATE TABLE public.products (
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
tenant_id VARCHAR(255) NOT NULL,
name VARCHAR(255) NOT NULL,
category VARCHAR(100)
);
---
Applications generate tokens with generate-db-connect-auth-token (NOT the admin variant):
# Application connection — uses DbConnect
PGPASSWORD="$(aws dsql generate-db-connect-auth-token \
--hostname ${CLUSTER_ENDPOINT} \
--region ${REGION})" \
psql -h ${CLUSTER_ENDPOINT} -U app_readwrite -d postgres
Set the search path to the correct schema after connecting:
SET search_path TO users_schema, public;
---
| Component | Database Role | Permissions | Schema Access |
|-----------|---------------|-------------|---------------|
| Web API (read) | api_readonly | SELECT | public |
| Web API (write) | api_readwrite | SELECT, INSERT, UPDATE, DELETE | public |
| User service | user_service | SELECT, INSERT, UPDATE | users_schema, public |
| Reporting | reporting_readonly | SELECT | public, users_schema |
| Admin setup | admin | ALL (setup only) | ALL |
---
-- Revoke database permissions
REVOKE ALL ON ALL TABLES IN SCHEMA users_schema FROM app_readonly;
REVOKE USAGE ON SCHEMA users_schema FROM app_readonly;
-- Revoke IAM mapping
AWS IAM REVOKE app_readonly FROM 'arn:aws:iam::*:role/AppReadOnlyRole';
---