Skip to content

Conversation

@tun0
Copy link
Contributor

@tun0 tun0 commented Dec 30, 2021

module "roles" {
  source = "../"

  connection_name = "foo"
  admin_username  = "foo"
  admin_password  = "foo"
  roles = {
    "role1": {
      databases_ro = ["db1", "db2"]
      databases_rw = []
    },
    "role2": {
      databases_ro = []
      databases_rw = ["db1", "db2"]
    },
    "role3": {
      databases_ro = ["db1"]
      databases_rw = ["db2"]
    },
  }
}

yields:

Terraform used the selected providers to generate the following execution plan. Resource actions are indicated with the following symbols:
  + create

Terraform will perform the following actions:

  # module.roles.postgresql_default_privileges.role_ro["db1.role2"] will be created
  + resource "postgresql_default_privileges" "role_ro" {
      + database          = "db1"
      + id                = (known after apply)
      + object_type       = "table"
      + owner             = "role2"
      + privileges        = [
          + "SELECT",
        ]
      + role              = "db1_role_ro"
      + schema            = "public"
      + with_grant_option = false
    }

  # module.roles.postgresql_default_privileges.role_ro["db2.role2"] will be created
  + resource "postgresql_default_privileges" "role_ro" {
      + database          = "db2"
      + id                = (known after apply)
      + object_type       = "table"
      + owner             = "role2"
      + privileges        = [
          + "SELECT",
        ]
      + role              = "db2_role_ro"
      + schema            = "public"
      + with_grant_option = false
    }

  # module.roles.postgresql_default_privileges.role_ro["db2.role3"] will be created
  + resource "postgresql_default_privileges" "role_ro" {
      + database          = "db2"
      + id                = (known after apply)
      + object_type       = "table"
      + owner             = "role3"
      + privileges        = [
          + "SELECT",
        ]
      + role              = "db2_role_ro"
      + schema            = "public"
      + with_grant_option = false
    }

  # module.roles.postgresql_default_privileges.role_rw["db1.role2"] will be created
  + resource "postgresql_default_privileges" "role_rw" {
      + database          = "db1"
      + id                = (known after apply)
      + object_type       = "table"
      + owner             = "role2"
      + privileges        = [
          + "DELETE",
          + "INSERT",
          + "REFERENCES",
          + "SELECT",
          + "TRIGGER",
          + "TRUNCATE",
          + "UPDATE",
        ]
      + role              = "db1_role_rw"
      + schema            = "public"
      + with_grant_option = false
    }

  # module.roles.postgresql_default_privileges.role_rw["db2.role2"] will be created
  + resource "postgresql_default_privileges" "role_rw" {
      + database          = "db2"
      + id                = (known after apply)
      + object_type       = "table"
      + owner             = "role2"
      + privileges        = [
          + "DELETE",
          + "INSERT",
          + "REFERENCES",
          + "SELECT",
          + "TRIGGER",
          + "TRUNCATE",
          + "UPDATE",
        ]
      + role              = "db2_role_rw"
      + schema            = "public"
      + with_grant_option = false
    }

  # module.roles.postgresql_default_privileges.role_rw["db2.role3"] will be created
  + resource "postgresql_default_privileges" "role_rw" {
      + database          = "db2"
      + id                = (known after apply)
      + object_type       = "table"
      + owner             = "role3"
      + privileges        = [
          + "DELETE",
          + "INSERT",
          + "REFERENCES",
          + "SELECT",
          + "TRIGGER",
          + "TRUNCATE",
          + "UPDATE",
        ]
      + role              = "db2_role_rw"
      + schema            = "public"
      + with_grant_option = false
    }

  # module.roles.postgresql_grant.role_ro["db1"] will be created
  + resource "postgresql_grant" "role_ro" {
      + database          = "db1"
      + id                = (known after apply)
      + object_type       = "table"
      + privileges        = [
          + "SELECT",
        ]
      + role              = "db1_role_ro"
      + schema            = "public"
      + with_grant_option = false
    }

  # module.roles.postgresql_grant.role_ro["db2"] will be created
  + resource "postgresql_grant" "role_ro" {
      + database          = "db2"
      + id                = (known after apply)
      + object_type       = "table"
      + privileges        = [
          + "SELECT",
        ]
      + role              = "db2_role_ro"
      + schema            = "public"
      + with_grant_option = false
    }

  # module.roles.postgresql_grant.role_rw["db1"] will be created
  + resource "postgresql_grant" "role_rw" {
      + database          = "db1"
      + id                = (known after apply)
      + object_type       = "table"
      + privileges        = [
          + "DELETE",
          + "INSERT",
          + "REFERENCES",
          + "SELECT",
          + "TRIGGER",
          + "TRUNCATE",
          + "UPDATE",
        ]
      + role              = "db1_role_rw"
      + schema            = "public"
      + with_grant_option = false
    }

  # module.roles.postgresql_grant.role_rw["db2"] will be created
  + resource "postgresql_grant" "role_rw" {
      + database          = "db2"
      + id                = (known after apply)
      + object_type       = "table"
      + privileges        = [
          + "DELETE",
          + "INSERT",
          + "REFERENCES",
          + "SELECT",
          + "TRIGGER",
          + "TRUNCATE",
          + "UPDATE",
        ]
      + role              = "db2_role_rw"
      + schema            = "public"
      + with_grant_option = false
    }

  # module.roles.postgresql_role.role["role1"] will be created
  + resource "postgresql_role" "role" {
      + bypass_row_level_security = false
      + connection_limit          = -1
      + create_database           = false
      + create_role               = false
      + encrypted_password        = true
      + id                        = (known after apply)
      + inherit                   = true
      + login                     = true
      + name                      = "role1"
      + password                  = (sensitive value)
      + replication               = false
      + roles                     = [
          + "db1_role_ro",
          + "db2_role_ro",
        ]
      + skip_drop_role            = false
      + skip_reassign_owned       = false
      + superuser                 = false
      + valid_until               = "infinity"
    }

  # module.roles.postgresql_role.role["role2"] will be created
  + resource "postgresql_role" "role" {
      + bypass_row_level_security = false
      + connection_limit          = -1
      + create_database           = false
      + create_role               = false
      + encrypted_password        = true
      + id                        = (known after apply)
      + inherit                   = true
      + login                     = true
      + name                      = "role2"
      + password                  = (sensitive value)
      + replication               = false
      + roles                     = [
          + "db1_role_rw",
          + "db2_role_rw",
        ]
      + skip_drop_role            = false
      + skip_reassign_owned       = false
      + superuser                 = false
      + valid_until               = "infinity"
    }

  # module.roles.postgresql_role.role["role3"] will be created
  + resource "postgresql_role" "role" {
      + bypass_row_level_security = false
      + connection_limit          = -1
      + create_database           = false
      + create_role               = false
      + encrypted_password        = true
      + id                        = (known after apply)
      + inherit                   = true
      + login                     = true
      + name                      = "role3"
      + password                  = (sensitive value)
      + replication               = false
      + roles                     = [
          + "db1_role_ro",
          + "db2_role_rw",
        ]
      + skip_drop_role            = false
      + skip_reassign_owned       = false
      + superuser                 = false
      + valid_until               = "infinity"
    }

  # module.roles.postgresql_role.role_ro["db1"] will be created
  + resource "postgresql_role" "role_ro" {
      + bypass_row_level_security = false
      + connection_limit          = -1
      + create_database           = false
      + create_role               = false
      + encrypted_password        = true
      + id                        = (known after apply)
      + inherit                   = true
      + login                     = false
      + name                      = "db1_role_ro"
      + replication               = false
      + skip_drop_role            = false
      + skip_reassign_owned       = false
      + superuser                 = false
      + valid_until               = "infinity"
    }

  # module.roles.postgresql_role.role_ro["db2"] will be created
  + resource "postgresql_role" "role_ro" {
      + bypass_row_level_security = false
      + connection_limit          = -1
      + create_database           = false
      + create_role               = false
      + encrypted_password        = true
      + id                        = (known after apply)
      + inherit                   = true
      + login                     = false
      + name                      = "db2_role_ro"
      + replication               = false
      + skip_drop_role            = false
      + skip_reassign_owned       = false
      + superuser                 = false
      + valid_until               = "infinity"
    }

  # module.roles.postgresql_role.role_rw["db1"] will be created
  + resource "postgresql_role" "role_rw" {
      + bypass_row_level_security = false
      + connection_limit          = -1
      + create_database           = false
      + create_role               = false
      + encrypted_password        = true
      + id                        = (known after apply)
      + inherit                   = true
      + login                     = false
      + name                      = "db1_role_rw"
      + replication               = false
      + skip_drop_role            = false
      + skip_reassign_owned       = false
      + superuser                 = false
      + valid_until               = "infinity"
    }

  # module.roles.postgresql_role.role_rw["db2"] will be created
  + resource "postgresql_role" "role_rw" {
      + bypass_row_level_security = false
      + connection_limit          = -1
      + create_database           = false
      + create_role               = false
      + encrypted_password        = true
      + id                        = (known after apply)
      + inherit                   = true
      + login                     = false
      + name                      = "db2_role_rw"
      + replication               = false
      + skip_drop_role            = false
      + skip_reassign_owned       = false
      + superuser                 = false
      + valid_until               = "infinity"
    }

  # module.roles.random_password.role["role1"] will be created
  + resource "random_password" "role" {
      + id          = (known after apply)
      + length      = 48
      + lower       = true
      + min_lower   = 0
      + min_numeric = 0
      + min_special = 0
      + min_upper   = 0
      + number      = true
      + result      = (sensitive value)
      + special     = false
      + upper       = true
    }

  # module.roles.random_password.role["role2"] will be created
  + resource "random_password" "role" {
      + id          = (known after apply)
      + length      = 48
      + lower       = true
      + min_lower   = 0
      + min_numeric = 0
      + min_special = 0
      + min_upper   = 0
      + number      = true
      + result      = (sensitive value)
      + special     = false
      + upper       = true
    }

  # module.roles.random_password.role["role3"] will be created
  + resource "random_password" "role" {
      + id          = (known after apply)
      + length      = 48
      + lower       = true
      + min_lower   = 0
      + min_numeric = 0
      + min_special = 0
      + min_upper   = 0
      + number      = true
      + result      = (sensitive value)
      + special     = false
      + upper       = true
    }

Plan: 20 to add, 0 to change, 0 to destroy.

@tun0 tun0 requested a review from EvyBongers December 30, 2021 11:12
Copy link
Contributor

@EvyBongers EvyBongers left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Perhaps best to also add a README

required_providers {
postgresql = {
source = "cyrilgdn/postgresql"
version = "1.14.0"
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

What's the requirement for pinning the exact version?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Primarily because it was copy/pasted from https://registry.terraform.io/providers/cyrilgdn/postgresql/latest/docs 😉

Also, in a next iteration all resource arguments will (most likely) be configured explicitly (no more relying on upstream defaults). This "requires" pinning to a specific version to avoid "drift" (new arguments being introduced for example).

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Development docs state that modules should only restrict minimum version.

Do not use ~> (or other maximum-version constraints) for modules you intend to reuse across many configurations, even if you know the module isn't compatible with certain newer versions. Doing so can sometimes prevent errors, but more often it forces users of the module to update many modules simultaneously when performing routine upgrades.

https://www.terraform.io/language/providers/requirements#version-constraints

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I get what they're getting at there. But I'm also leaning towards keeping this as-is, as the given recommendation is mostly aimed at large scale deployments. In our case I think having the little bit of additional control over the version actually benefits us. Can always make it more liberal later on when needed. The reverse would likely be way less trivial.

@tun0 tun0 requested a review from EvyBongers December 30, 2021 12:25
required_providers {
postgresql = {
source = "cyrilgdn/postgresql"
version = "1.14.0"
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Development docs state that modules should only restrict minimum version.

Do not use ~> (or other maximum-version constraints) for modules you intend to reuse across many configurations, even if you know the module isn't compatible with certain newer versions. Doing so can sometimes prevent errors, but more often it forces users of the module to update many modules simultaneously when performing routine upgrades.

https://www.terraform.io/language/providers/requirements#version-constraints

@@ -0,0 +1,484 @@
Output:
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Maybe mention the caveat that this module and required provider config can only be added once the referred database instance has already been created? Either here or in the README at repo root.

@tun0 tun0 requested a review from EvyBongers December 30, 2021 13:42
@tun0 tun0 merged commit 5020c4f into main Dec 30, 2021
@tun0 tun0 deleted the first-iteration branch December 30, 2021 13:55
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants