Skip to content

Commit be3226d

Browse files
committed
docs: streamline readme
1 parent e4d03b9 commit be3226d

File tree

4 files changed

+257
-42
lines changed

4 files changed

+257
-42
lines changed

README.md

Lines changed: 97 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -1,18 +1,26 @@
11
![Banner][banner-image]](https://masterpoint.io/)
22

3-
# terraform-postgres-logical-dbs
3+
# terraform-postgres-dbs-roles
44

55
[![Release][release-badge]][latest-release]
66

77
💡 Learn more about Masterpoint [below](#who-we-are-𐦂𖨆𐀪𖠋).
88

99
## Purpose and Functionality
1010

11-
This repository serves as a child module for managing Postgres Logical Databases.
11+
This repository serves as a child module for managing Postgres Logical Databases and Roles
1212

1313
## Usage
1414

15-
```terraform
15+
### Prerequisites
16+
17+
1. Postgres user with `LOGIN` privileges
18+
19+
### Example
20+
21+
Please see [examples/complete] for a working example.
22+
23+
```hcl
1624
provider "postgresql" {
1725
scheme = "postgres"
1826
host = "localhost"
@@ -24,34 +32,102 @@ provider "postgresql" {
2432
}
2533
2634
module "logical_dbs" {
27-
source = "git::https://github.com/masterpointio/terraform-postgres-logical-dbs.git"
28-
# Masterpoint recommends pinning every module to a specific version
29-
# version = "x.x.x"
35+
source = "git::https://github.com/masterpointio/terraform-postgres-dbs-roles.git?ref=main"
3036
3137
databases = [
3238
{
33-
name = "app1_db"
34-
connection_limit = 10
35-
},
36-
{
37-
name = "app2_db"
38-
connection_limit = 20
39+
name = "app"
40+
connection_limit = -1
3941
}
4042
]
41-
}
42-
```
4343
44-
### Prerequisites
44+
roles = [
45+
{
46+
role = {
47+
name = "system_user"
48+
login = true
49+
superuser = false
50+
password = "insecure-pass-for-demo-app"
51+
}
52+
53+
table_grants = {
54+
role = "system_user"
55+
database = "app"
56+
schema = "public"
57+
object_type = "table"
58+
objects = [] # empty list to grant all tables
59+
privileges = ["ALL"]
60+
}
61+
62+
schema_grants = {
63+
role = "system_user"
64+
database = "app"
65+
schema = "public"
66+
object_type = "schema"
67+
privileges = ["USAGE", "CREATE"]
68+
}
4569
46-
To use this terraform module, you'll need to have a postgres user capable of,
70+
sequence_grants = {
71+
role = "system_user"
72+
database = "app"
73+
schema = "public"
74+
object_type = "sequence"
75+
objects = [] # empty list to grant all sequences
76+
privileges = ["ALL"]
77+
}
78+
},
79+
{
80+
role = {
81+
name = "readonly_user"
82+
login = true
83+
password = "insecure-pass-for-demo-readonly"
84+
superuser = false
85+
}
4786
48-
- logging into the Postgres server
49-
- creating Postgres databases.
50-
There are example Postgres CLI commands for creating a user with these privileges in `examples/complete/fixtures.tfvars`.
87+
table_grants = {
88+
role = "readonly_user"
89+
database = "app"
90+
schema = "public"
91+
object_type = "table"
92+
objects = [] # empty list to grant all tables
93+
privileges = ["SELECT"]
94+
}
5195
52-
### Step-by-Step Instructions
96+
sequence_grants = {
97+
role = "readonly_user"
98+
database = "app"
99+
schema = "public"
100+
object_type = "sequence"
101+
objects = [] # empty list to grant all sequences
102+
privileges = ["USAGE", "SELECT"]
103+
}
104+
105+
default_privileges = [
106+
{
107+
role = "readonly_user"
108+
database = "app"
109+
schema = "public"
110+
owner = "system_user"
111+
object_type = "table"
112+
objects = [] # empty list to grant all tables
113+
privileges = ["SELECT"]
114+
},
115+
{
116+
role = "readonly_user"
117+
database = "app"
118+
schema = "public"
119+
owner = "system_user"
120+
object_type = "sequence"
121+
objects = [] # empty list to grant all sequences
122+
privileges = ["USAGE", "SELECT"]
123+
},
124+
]
125+
}
126+
]
127+
128+
}
129+
```
53130

54-
TODO - do we need this section? If so, what's missing that I should fill in?
55131
<!-- BEGINNING OF PRE-COMMIT-TERRAFORM DOCS HOOK -->
56132

57133
## Requirements

examples/complete/fixtures.tfvars

Lines changed: 19 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -13,40 +13,40 @@ db_sslmode = "disable"
1313

1414
databases = [
1515
{
16-
name = "app1_db"
16+
name = "app"
1717
connection_limit = 10
1818
}
1919
]
2020

2121
roles = [
2222
{
2323
role = {
24-
name = "app1_app_user"
24+
name = "system_user"
2525
login = true
2626
superuser = false
2727
password = "insecure-pass-for-demo-app"
2828
}
2929

3030
table_grants = {
31-
role = "app1_app_user"
32-
database = "app1_db"
31+
role = "system_user"
32+
database = "app"
3333
schema = "public"
3434
object_type = "table"
3535
objects = [] # empty list to grant all tables
3636
privileges = ["ALL"]
3737
}
3838

3939
schema_grants = {
40-
role = "app1_app_user"
41-
database = "app1_db"
40+
role = "system_user"
41+
database = "app"
4242
schema = "public"
4343
object_type = "schema"
4444
privileges = ["USAGE", "CREATE"]
4545
}
4646

4747
sequence_grants = {
48-
role = "app1_app_user"
49-
database = "app1_db"
48+
role = "system_user"
49+
database = "app"
5050
schema = "public"
5151
object_type = "sequence"
5252
objects = [] # empty list to grant all sequences
@@ -55,24 +55,24 @@ roles = [
5555
},
5656
{
5757
role = {
58-
name = "app1_readonly_user"
58+
name = "readonly_user"
5959
login = true
6060
password = "insecure-pass-for-demo-readonly"
6161
superuser = false
6262
}
6363

6464
table_grants = {
65-
role = "app1_readonly_user"
66-
database = "app1_db"
65+
role = "readonly_user"
66+
database = "app"
6767
schema = "public"
6868
object_type = "table"
6969
objects = [] # empty list to grant all tables
7070
privileges = ["SELECT"]
7171
}
7272

7373
sequence_grants = {
74-
role = "app1_readonly_user"
75-
database = "app1_db"
74+
role = "readonly_user"
75+
database = "app"
7676
schema = "public"
7777
object_type = "sequence"
7878
objects = [] # empty list to grant all sequences
@@ -81,19 +81,19 @@ roles = [
8181

8282
default_privileges = [
8383
{
84-
role = "app1_readonly_user"
85-
database = "app1_db"
84+
role = "readonly_user"
85+
database = "app"
8686
schema = "public"
87-
owner = "app1_app_user"
87+
owner = "system_user"
8888
object_type = "table"
8989
objects = [] # empty list to grant all tables
9090
privileges = ["SELECT"]
9191
},
9292
{
93-
role = "app1_readonly_user"
94-
database = "app1_db"
93+
role = "readonly_user"
94+
database = "app"
9595
schema = "public"
96-
owner = "app1_app_user"
96+
owner = "system_user"
9797
object_type = "sequence"
9898
objects = [] # empty list to grant all sequences
9999
privileges = ["USAGE", "SELECT"]
Lines changed: 129 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,129 @@
1+
import sqlalchemy as sa
2+
3+
4+
# authenticate to postgres using the system_user
5+
engine = sa.create_engine(
6+
"postgresql://system_user:insecure-pass-for-demo-app@localhost:5432/app"
7+
)
8+
9+
engine_ro = sa.create_engine(
10+
"postgresql://readonly_user:insecure-pass-for-demo-readonly@localhost:5432/app"
11+
)
12+
13+
14+
15+
sql = "SELECT 1"
16+
17+
with engine.connect() as conn:
18+
result = conn.execute(sa.text(sql))
19+
assert result.fetchall() == [(1,)]
20+
21+
# create tables
22+
create_tables_sqls = []
23+
24+
create_tables_sql_users = """
25+
CREATE TABLE IF NOT EXISTS users (
26+
id SERIAL PRIMARY KEY,
27+
username VARCHAR(50) NOT NULL UNIQUE,
28+
email VARCHAR(100) NOT NULL UNIQUE,
29+
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP
30+
)
31+
"""
32+
create_tables_sqls.append(create_tables_sql_users)
33+
34+
create_tables_sql_categories = """
35+
CREATE TABLE IF NOT EXISTS categories (
36+
id SERIAL PRIMARY KEY,
37+
name VARCHAR(50) NOT NULL UNIQUE,
38+
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP
39+
)
40+
"""
41+
create_tables_sqls.append(create_tables_sql_categories)
42+
43+
create_tables_sql_todos = """
44+
CREATE TABLE IF NOT EXISTS todos (
45+
id SERIAL PRIMARY KEY,
46+
user_id INTEGER REFERENCES users(id) ON DELETE CASCADE,
47+
category_id INTEGER REFERENCES categories(id),
48+
title VARCHAR(100) NOT NULL,
49+
description TEXT,
50+
due_date DATE,
51+
completed BOOLEAN DEFAULT FALSE,
52+
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP
53+
);
54+
"""
55+
create_tables_sqls.append(create_tables_sql_todos)
56+
57+
print("Creating tables")
58+
with engine.connect() as conn:
59+
for sql in create_tables_sqls:
60+
conn.execute(sa.text(sql))
61+
conn.commit()
62+
print("Tables created")
63+
64+
65+
# list all tables in the app database
66+
sql = "SELECT table_name FROM information_schema.tables WHERE table_schema = 'public'"
67+
68+
print("Validating tables")
69+
with engine.connect() as conn:
70+
result = conn.execute(sa.text(sql))
71+
tables = result.fetchall()
72+
print(f"Tables from the database: {tables}")
73+
assert tables == [('users',), ('categories',), ('todos',)], "Tables are not created"
74+
print("Tables validated")
75+
76+
# Insert test data
77+
insert_users_sqls = [
78+
"""
79+
INSERT INTO users (id, username, email) VALUES
80+
(1, 'alice', 'alice@example.com'),
81+
(2, 'bob', 'bob@example.com'),
82+
(3, 'charlie', 'charlie@example.com');
83+
""",
84+
85+
"""
86+
INSERT INTO categories (id, name) VALUES
87+
(1, 'Work'),
88+
(2, 'Personal'),
89+
(3, 'Errands');
90+
""",
91+
92+
"""
93+
INSERT INTO todos (user_id, category_id, title, description, due_date, completed) VALUES
94+
(1, 1, 'Finish project', 'Complete the infrastructure project.', '2025-04-30', FALSE),
95+
(2, 2, 'Buy groceries', 'Milk, Bread, Eggs', '2025-04-23', FALSE),
96+
(3, 3, 'Doctor appointment', 'Annual check-up at 3 PM.', '2025-04-25', FALSE),
97+
(1, 2, 'Call mom', 'Check in with family.', '2025-04-24', FALSE),
98+
(2, 1, 'Write blog post', 'Post about PostgreSQL role management.', '2025-04-27', TRUE);
99+
"""
100+
]
101+
102+
print("Inserting test data")
103+
with engine.connect() as conn:
104+
# drop existing data
105+
conn.execute(sa.text("TRUNCATE TABLE todos CASCADE"))
106+
conn.commit()
107+
108+
conn.execute(sa.text("TRUNCATE TABLE categories CASCADE"))
109+
conn.commit()
110+
111+
conn.execute(sa.text("TRUNCATE TABLE users CASCADE"))
112+
conn.commit()
113+
114+
115+
for sql in insert_users_sqls:
116+
conn.execute(sa.text(sql))
117+
conn.commit()
118+
print("Test data inserted")
119+
120+
# Query Users
121+
sql = "SELECT username, email FROM users"
122+
123+
print("Validating users")
124+
with engine.connect() as conn:
125+
result = conn.execute(sa.text(sql))
126+
users = result.fetchall()
127+
print(f"Users from the database: {users}")
128+
assert users == [('alice', 'alice@example.com'), ('bob', 'bob@example.com'), ('charlie', 'charlie@example.com')]
129+
print("Users validated")

0 commit comments

Comments
 (0)