Skip to content

Repository files navigation

pg-create

PostgreSQL Resource Provisioning CLI - A safe, idempotent tool for creating and managing PostgreSQL databases, roles, schemas, extensions, and grants.

Features

  • Idempotent Operations - Safe to run multiple times
  • Flexible Configuration - Use CLI flags or YAML/JSON config files
  • Dry Run Mode - Preview changes before applying
  • Environment Safeguards - Confirmation prompts for prod/qa
  • Role-based Privileges - Automatic privilege templates (app, ro, dba)
  • Security-focused SQL handling - identifier quoting and input validation for dynamic statements
  • Clear Output - Visual indicators for created vs existing resources

Included CLIs

This repository contains several Go command-line tools:

  • pg-create – Primary provisioning CLI for creating and managing PostgreSQL databases, roles, schemas, extensions, and grants.
  • pg-validate – Validation CLI for inspecting role and grant configuration across one or more databases.
  • pgstress – Connection stress test CLI for opening many concurrent PostgreSQL sessions and measuring instance behavior.
  • pg-watch – Monitoring CLI for querying pg_stat_activity repeatedly, optionally refreshing every second.

Installation

Build from Source

# Clone the repository
git clone https://github.com/ChaosHour/pg-create.git
cd pg-create
# Build using Makefile
make build
# Binary will be in ./bin/pg-create

Install to GOPATH

make install

Usage

Using CLI Flags

./bin/pg-create \
-s localhost \
-u postgres \
-p your_password \
-d myapp_prod \
-sc myapp,ext \
-r myapp_dba:pass1:dba,myapp_app:pass2:app,myapp_ro:pass3:ro \
-e uuid-ossp,pg_trgm,hstore \
-sp "myapp, ext" \
-g usage,select,insert,update,delete,execute \
-env standalone

Using Config File (YAML)

# Create your config file (see config.example.yaml)
cp config.example.yaml config.yaml
# Edit with your settings
vim config.yaml
# Run with config
./bin/pg-create -c config.yaml

Using Config File (JSON)

# Create your config file (see config.example.json)
cp config.example.json config.json
# Edit with your settings
vim config.json
# Run with config
./bin/pg-create -c config.json

Dry Run Mode

Preview what changes would be made without executing them:

./bin/pg-create -c config.yaml -dry-run

Note: Go's flag parser accepts both -dry-run and --dry-run.

Configuration

CLI Flags

FlagDescriptionRequired
-sPostgreSQL hostYes (unless -c is used)
-uAdmin user for connectionYes (unless -c is used)
-pAdmin password (optional when ~/.pgpass matches)No
-dDatabase name to createYes (unless -c is used)
-portPort (default: 5432)No
-scComma-separated schemasNo
-rRoles (name:password:type, type is `appro
-eComma-separated extensionsNo
-spSearch path (comma-separated schemas)No
-gComma-separated grantsNo
-envEnvironment (`standaloneqa
-cConfig file (YAML/JSON)No
-dry-runPreview mode (no changes applied)No
-hShow helpNo

Role Types

  • dba - Database administrator (full privileges, 10 connections)
  • app - Application role (CRUD privileges, unlimited connections)
  • ro - Read-only role (SELECT only, 10 connections)

Input Validation

Invalid role types and grants fail fast.

# Invalid role type example
./bin/pg-create -s localhost -u postgres -d myapp -r app_user:secret:readonly
# Output: Invalid configuration: invalid role type "readonly" for role "app_user" (must be app, ro, or dba)# Invalid grant example
./bin/pg-create -s localhost -u postgres -d myapp -g select,truncate
# Output: Invalid configuration: invalid grant type "truncate"

Grant Types

  • usage - Schema usage
  • select - Read tables and sequences
  • insert - Insert into tables
  • update - Update tables and sequences
  • delete - Delete from tables
  • execute - Execute functions

Provisioning Flow

The CLI follows this order:

  1. Create Database - If not exists
  2. Create Roles - With passwords and connection limits
  3. Create Schemas - If not exists
  4. Create Extensions - In specified schema
  5. Apply Grants - Based on grant types
  6. Set Search Paths - For each role
  7. Apply Default Privileges - Based on role type

Examples

Simple Database Setup

./bin/pg-create \
-s localhost -u postgres -p secret \
-d testdb -sc public

Full Application Setup

# config.yamlhost: localhostport: 5432user: postgrespassword: admin_passdatabase: myapp_prodenvironment: prodschemas:
- myapp
- extroles:
- name: myapp_dbapassword: dba_secure_passtype: dba
- name: myapp_apppassword: app_secure_passtype: app
- name: myapp_ropassword: ro_secure_passtype: roextensions:
- uuid-ossp
- pg_trgmgrants:
- usage
- select
- insert
- update
- delete
- executesearch_path: myapp, ext
./bin/pg-create -c config.yaml

Grant Validation

Use the SQL validation toolkit in sql/validation to verify database, schema, table, sequence, function, and default privileges after provisioning.

1) Seed validation objects (optional but recommended)

psql -h localhost -U postgres -d myapp_prod \
-v schema='myapp' \
-f sql/validation/seed_validation_objects.sql

2) Validate grants for one or more roles

psql -h localhost -U postgres -d myapp_prod \
-v roles='myapp_app,myapp_ro,myapp_dba' \
-v schema='myapp' \
-f sql/validation/validate_grants.sql

Notes:

  • roles defaults to readonly when omitted.
  • schema defaults to empty, which validates all non-system schemas.
  • If a schema has no objects yet, object-level result sets can be empty.

Validation CLI

pg-validate is a second CLI in this repo for role/user grant inspection.

Build

make build-validate

Usage

./bin/pg-validate \
-s localhost \
-u postgres \
-db myapp_prod \
-roles myapp_app,myapp_ro,myapp_dba \
-schema myapp

Key flags:

  • -s: host
  • -port: port (default 5432)
  • -u: admin user for inspection queries
  • -p: admin password (optional when ~/.pgpass matches)
  • -db: database to inspect
  • -roles: one role or comma-separated role list
  • -schema: optional schema filter

Development

Project Structure

pg-create/
├── cmd/
│ └── pgcreate/
│ └── main.go # Entry point
│ └── pgvalidate/
│ └── main.go # Validation CLI entry point
├── pkg/
│ ├── config/
│ │ └── config.go # Configuration handling
│ └── database/
│ └── provisioner.go # Database provisioning logic
│ └── validator/
│ └── validator.go # Validation report logic
├── sql/
│ └── validation/
│ ├── validate_grants.sql
│ ├── seed_validation_objects.sql
│ └── README.md
├── bin/ # Build output
├── Makefile # Build automation
├── config.example.yaml # Example YAML config
├── config.example.json # Example JSON config
└── README.md

Build Commands

make build # Build binary
make build-validate # Build validator CLI
make build-all # Build both CLIs
make clean # Clean artifacts
make deps # Install dependencies
make test# Run tests
make install # Install to GOPATH
make help# Show all targets

Security Considerations

  • Never commit config files with real passwords
  • Use .pgpass file for credentials when possible
  • Always use -dry-run first in production
  • The CLI will prompt for confirmation in prod/qa environments
  • Dynamic SQL paths use identifier quoting and validated inputs; avoid untrusted config values

Troubleshooting

Common Errors

Error messageLikely causeFix
Missing required flags: -s (host), -u (user), -d (database)Required flags were not provided when not using -cProvide -s, -u, and -d, or use -c config.yaml
No password provided: use -p flag or add a matching entry to ~/.pgpassNo -p and no matching ~/.pgpass entryAdd -p, or add host:port:postgres:user:password to ~/.pgpass
Failed to load config file: ...Config file path invalid or unreadableVerify path and file permissions
unsupported config file format: ...Config file extension is not .yaml, .yml, or .jsonRename or convert config file to supported format
Invalid configuration: invalid role type "..."Role type not one of app, ro, dbaUpdate role spec or config to one of supported role types
Invalid configuration: invalid grant type "..."Unsupported grant valueUse only usage, select, insert, update, delete, execute
failed to connect to database: ...Host/port/user/password/network issueValidate connection info with psql and pg_isready
invalid search_path "...": must contain at least one schema-sp/search_path is empty or contains only commas/spacesSet a valid schema list, for example -sp "myapp,public"
Operation cancelled by userConfirmation prompt was answered no in qa/prodRe-run and confirm with yes

Connection Issues

Ensure your PostgreSQL server allows connections:

# Check PostgreSQL is running
pg_isready -h localhost
# Test connection
psql -h localhost -U postgres -d postgres

Permission Errors

Ensure the admin user has sufficient privileges:

-- Check your roleSELECTcurrent_user, session_user;
-- Check privileges
\du

License

MIT License - See LICENSE file for details

Contributing

Contributions welcome! Please open an issue or PR.

About

Create PostgreSQL Users, Grants and Roles

Resources

Stars

1 star

Watchers

1 watching

Forks

Releases

Packages

Used by

Contributors

Languages