Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -2,5 +2,5 @@
node_modules/
/.env
.vscode/
rds-db-init.zip
rds-db-init*.zip

1 change: 1 addition & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ Yet-hosted lambda code for initializing RDS Postgres databases without leaving C
| DBHost | Primary endpoint of RDS DB cluster. |
| DBPort | Port to access RDS DB on. |
| DBName | Name of RDS DB to grant user permissions on. |
| DBSchema | Schema to grant the user `USAGE` and `CREATE` on. Defaults to `public`. |

## Supported Regions

Expand Down
34 changes: 19 additions & 15 deletions index.js
Original file line number Diff line number Diff line change
@@ -1,17 +1,15 @@
const { Client } = require('pg')
var pgformat = require('pg-format');
const AWS = require("aws-sdk");
const sm = new AWS.SSM();
const { SSMClient, GetParameterCommand } = require("@aws-sdk/client-ssm");
const cfnr = require('./cfn-response.js');

const client = new SSMClient({});
//helper to grab and parse secure strings from ssm
const getParam = async (path, secure) => {
try {
const param = await sm.getParameter({
Name: path,
WithDecryption: secure,
}).promise();
return await param.Parameter.Value;
const command = new GetParameterCommand({ Name: path, WithDecryption: secure });
const data = await client.send(command);
return data.Parameter.Value;
} catch (e) {
console.log(e);
return null;
Expand All @@ -31,18 +29,21 @@ exports.handler = async (event, context) => {
const appUser = input.DBUsername;
const appPass = await getParam(input.DBPasswordPath, true);
const db = input.DBName;
const schema = input.DBSchema || 'public';
const client = new Client({
host: input.DBHost,
port: input.DBPort,
database: db,
user: input.DBMasterUsername,
password: await getParam(input.DBMasterPasswordPath, true)
password: await getParam(input.DBMasterPasswordPath, true),
ssl: { rejectUnauthorized: false }
});

//needed to use a pg query formtter because you can't use identifiers as vars in prepared statements
const checkQuery = 'SELECT FROM pg_catalog.pg_roles WHERE rolname = $1::text';
const createQuery = pgformat('CREATE USER %I WITH ENCRYPTED PASSWORD %L', appUser, appPass);
const grantQuery = pgformat('GRANT ALL PRIVILEGES ON DATABASE %I TO %I', db, appUser);
const grantDatabaseQuery = pgformat('GRANT ALL PRIVILEGES ON DATABASE %I TO %I', db, appUser);
const grantSchemaQuery = pgformat('GRANT USAGE, CREATE ON SCHEMA %I TO %I', schema, appUser);

try {
console.log("Attempting database connection");
Expand All @@ -56,19 +57,22 @@ exports.handler = async (event, context) => {
if (userResponse.rowCount < 1) {
//create user
console.log("Creating User");
const createResponse = await client.query(createQuery);
//grant db priv to user
console.log("Granting privileges to user.");
const grantResponse = await client.query(grantQuery);
await client.query(createQuery);
} else {
console.log("User already exists. Exiting.");
console.log("User already exists.");
}
// Reconcile privileges on every create or update so existing users
// receive grants added by newer versions of this function.
console.log("Granting database privileges to user.");
await client.query(grantDatabaseQuery);
console.log("Granting schema privileges to user.");
await client.query(grantSchemaQuery);
await client.query("COMMIT");
} catch(err) {
console.log("db init transaction failed");
console.error(err);
await client.query("ROLLBACK");
await cfnr.send(event, context, cfnr.FAILED);
return await cfnr.send(event, context, cfnr.FAILED);
}
console.log("Finished db init");
await cfnr.send(event, context, cfnr.SUCCESS, {
Expand Down
203 changes: 68 additions & 135 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@
"author": "Yet Analytics, Inc.",
"license": "Apache-2.0",
"dependencies": {
"pg": "^8.9.0",
"pg": "^8.23.0",
"pg-format": "^1.0.4"
}
}