├── .github └── workflows │ ├── lint.yml │ ├── molecule.yml │ ├── release.yml │ └── trigger-release.yml ├── .gitignore ├── .yamllint ├── README.md ├── defaults └── main.yml ├── meta └── main.yml ├── molecule ├── _common │ ├── Dockerfile.j2 │ └── requirements.yml ├── create-destroy │ ├── converge.yml │ └── molecule.yml └── default │ ├── converge.yml │ ├── molecule.yml │ └── verify.yml └── tasks └── main.yml /.github/workflows/lint.yml: -------------------------------------------------------------------------------- 1 | name: ansible-lint 2 | on: [pull_request_target] 3 | jobs: 4 | ansible-lint: 5 | name: ansible-lint 6 | runs-on: ubuntu-latest 7 | steps: 8 | - uses: actions/checkout@v2 9 | 10 | - uses: actions/setup-python@v2 11 | with: 12 | python-version: '3.x' 13 | 14 | - name: ansible-lint 15 | uses: reviewdog/action-ansiblelint@v1 16 | with: 17 | github_token: ${{ secrets.GITHUB_TOKEN }} 18 | reporter: github-pr-review 19 | filter_mode: nofilter 20 | -------------------------------------------------------------------------------- /.github/workflows/molecule.yml: -------------------------------------------------------------------------------- 1 | --- 2 | name: Molecule Tests 3 | on: 4 | pull_request: 5 | push: 6 | branches: 7 | - main 8 | jobs: 9 | molecule: 10 | name: molecule 11 | runs-on: ubuntu-latest 12 | strategy: 13 | matrix: 14 | scenario: 15 | - default 16 | - create-destroy 17 | steps: 18 | - uses: actions/checkout@v2 19 | 20 | - uses: actions/setup-python@v2 21 | with: 22 | python-version: '3.x' 23 | 24 | - name: Install dependencies 25 | run: pip3 install molecule[docker] ansible-core 26 | 27 | - name: Run molecule 28 | run: molecule test -s ${{ matrix.scenario }} 29 | env: 30 | PY_COLORS: '1' 31 | ANSIBLE_FORCE_COLOR: '1' 32 | -------------------------------------------------------------------------------- /.github/workflows/release.yml: -------------------------------------------------------------------------------- 1 | --- 2 | # This workflow requires a GALAXY_API_KEY secret present in the GitHub 3 | # repository or organization. 4 | # 5 | # See: https://github.com/marketplace/actions/publish-ansible-role-to-galaxy 6 | # See: https://github.com/ansible/galaxy/issues/46 7 | 8 | name: Release 9 | 10 | 'on': 11 | push: 12 | tags: 13 | - '*' 14 | 15 | jobs: 16 | release: 17 | name: Release 18 | runs-on: ubuntu-latest 19 | steps: 20 | - name: Check out the codebase. 21 | uses: actions/checkout@v2 22 | 23 | - name: Set up Python 3. 24 | uses: actions/setup-python@v2 25 | with: 26 | python-version: '3.x' 27 | 28 | - name: Install Ansible. 29 | run: pip3 install ansible-core 30 | 31 | - name: Trigger a new import on Galaxy. 32 | run: ansible-galaxy role import --api-key ${{ secrets.GALAXY_API_KEY }} $(echo ${{ github.repository }} | cut -d/ -f1) $(echo ${{ github.repository }} | cut -d/ -f2) --branch main 33 | -------------------------------------------------------------------------------- /.github/workflows/trigger-release.yml: -------------------------------------------------------------------------------- 1 | --- 2 | # This workflow requires a GALAXY_API_KEY secret present in the GitHub 3 | # repository or organization. 4 | # 5 | # See: https://github.com/marketplace/actions/publish-ansible-role-to-galaxy 6 | # See: https://github.com/ansible/galaxy/issues/46 7 | 8 | name: "Bump 'n Release" 9 | 10 | on: 11 | workflow_dispatch: 12 | 13 | jobs: 14 | release: 15 | name: Release 16 | runs-on: ubuntu-latest 17 | steps: 18 | - name: Check out the codebase. 19 | uses: actions/checkout@v2 20 | with: 21 | fetch-depth: 0 22 | 23 | - name: Set up Python 3. 24 | uses: actions/setup-python@v2 25 | with: 26 | python-version: '3.x' 27 | 28 | - name: Install Ansible. 29 | run: pip3 install ansible-core 30 | 31 | - name: Create a new git tag 32 | run: | 33 | MOST_RECENT_TAG=$(git describe --tags --abbrev=0) 34 | major_minor=$(echo "$MOST_RECENT_TAG" | sed 's/\(.*\..*\.\)\(.*\)/\1/') 35 | patch=$(echo "$MOST_RECENT_TAG" | sed 's/\(.*\..*\.\)\(.*\)/\2/') 36 | newpatch=$(echo "$patch + 1" | bc) 37 | NEW_TAG="${major_minor}${newpatch}" 38 | echo "$MOST_RECENT_TAG -> $NEW_TAG" 39 | 40 | git config user.name github-actions 41 | git config user.email github-actions@github.com 42 | git tag "$NEW_TAG" 43 | git push --tags 44 | 45 | # We have to do this step as GHA prevents triggering it's own actions, to prevent runaway loops. 46 | - name: Trigger a new import on Galaxy. 47 | run: ansible-galaxy role import --api-key ${{ secrets.GALAXY_API_KEY }} $(echo ${{ github.repository }} | cut -d/ -f1) $(echo ${{ github.repository }} | cut -d/ -f2) --branch main 48 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | syntax: glob 2 | 3 | *~ 4 | *.*.swp 5 | -------------------------------------------------------------------------------- /.yamllint: -------------------------------------------------------------------------------- 1 | --- 2 | # Based on ansible-lint config 3 | extends: default 4 | 5 | rules: 6 | braces: 7 | max-spaces-inside: 1 8 | level: error 9 | brackets: 10 | max-spaces-inside: 1 11 | level: error 12 | colons: 13 | max-spaces-after: -1 14 | level: error 15 | commas: 16 | max-spaces-after: -1 17 | level: error 18 | comments: disable 19 | comments-indentation: disable 20 | document-start: disable 21 | empty-lines: 22 | max: 3 23 | level: error 24 | hyphens: 25 | level: error 26 | indentation: disable 27 | key-duplicates: enable 28 | line-length: disable 29 | new-line-at-end-of-file: disable 30 | new-lines: 31 | type: unix 32 | trailing-spaces: disable 33 | truthy: disable 34 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | PostgreSQL Objects 2 | ================== 3 | 4 | [![Ansible Role](https://img.shields.io/ansible/role/60839)](https://galaxy.ansible.com/galaxyproject/postgresql_objects) 5 | [![Molecule Tests](https://github.com/galaxyproject/ansible-postgresql-objects/actions/workflows/molecule.yml/badge.svg)](https://github.com/galaxyproject/ansible-postgresql-objects/actions/workflows/molecule.yml) 6 | 7 | PostgreSQL Objects is an [Ansible][ansible] role for managing PostgreSQL users, 8 | groups databases, and privileges. It is a small wrapper around the 9 | [`postgresql_user`][pguser], [`postgresql_db`][pgdb] and 10 | [`postgresql_privs`][pgprivs] standard modules provided with Ansible. Many 11 | PostgreSQL roles exist in [Ansible Galaxy][ansiblegalaxy] but none exist for 12 | only managing database objects without managing the server installation and 13 | configuration. For that, see [galaxyproject.postgresql][gxpostgresql]. 14 | 15 | [ansible]: http://www.ansible.com 16 | [pguser]: https://docs.ansible.com/ansible/latest/collections/community/postgresql/postgresql_user_module.html 17 | [pgdb]: https://docs.ansible.com/ansible/latest/collections/community/postgresql/postgresql_db_module.html 18 | [pgprivs]: https://docs.ansible.com/ansible/latest/collections/community/postgresql/postgresql_privs_module.html 19 | [ansiblegalaxy]: https://galaxy.ansible.com 20 | [gxpostgresql]: https://github.com/galaxyproject/ansible-postgresql/ 21 | 22 | Requirements 23 | ------------ 24 | 25 | This role has the same dependencies as the `postgresql_*` modules, namely, The 26 | Python [`psycopg2`][psycopg2] module. This can easily be installed via a 27 | pre-task in the same play as this role: 28 | 29 | ```yaml 30 | - hosts: dbservers 31 | pre_tasks: 32 | - name: Install psycopg2 33 | apt: 34 | name: "python{{ (ansible_python.version.major == 3) | ternary('3', '') }}-psycopg2" 35 | when: ansible_os_family == "Debian" 36 | - name: Install psycopg2 37 | yum: 38 | name: "python{{ ansible_python.version.major }}-psycopg2" 39 | become: yes 40 | when: ansible_os_family == 'RedHat' 41 | roles: 42 | - role: galaxyproject.postgresql_objects 43 | become: true 44 | become_user: postgres 45 | ``` 46 | 47 | Alternatively, the [galaxyproject.postgresql][gxpostgresql] role will (by default) install psycopg2 for you automatically. 48 | 49 | [psycopg2]: https://www.psycopg.org/ 50 | 51 | Role Variables 52 | -------------- 53 | 54 | Objects are configured via the following variables: 55 | 56 | - `postgresql_objects_users`: A list of PostgreSQL users to create or drop. 57 | List items are dictionaries, keys match the [`postgresql_user`][pguser] 58 | module parameters. 59 | - `postgresql_objects_groups`: A list of PostgreSQL groups to create or drop. 60 | List items are dictionaries, keys match the [`postgresql_user`][pguser] 61 | module parameters with the addition of the `users` key, which should itself 62 | be a list of users to add to the group. List items are dictionaries which 63 | should have the `name` key (required) and optionally the `state` key (whose 64 | values are either `present` (default) or `absent`). 65 | - `postgresql_objects_databases`: A list of databases to create or drop. List 66 | items are dictionaries, keys match the [`postgresql_db`][pgdb] module 67 | parameters. 68 | - `postgresql_objects_privileges`: A list of privileges to grant or revoke. 69 | List items are dictionaries, keys match the [`postgresql_privs`][pgprivs] 70 | module parameters. 71 | - `postgresql_objects_ignore_revoke_failure`: True or false. Revoking 72 | privileges from a nonexistent user, database, or table would normally cause a 73 | failure since PostgreSQL will return an error on such an operation. If not 74 | handled, this would prevent this role from being used idempotently. See 75 | caveats below. 76 | 77 | Additional variables (`postgresql_objects_login_host`, 78 | `postgresql_objects_login_user`, `postgresql_objects_login_password` and 79 | `postgresql_objects_port`) control how to connect to the database as a user 80 | that has privileges to perform the requested changes. However, these can be 81 | left unset if you use a system user with administrative privileges in 82 | PostgreSQL, (such as with `sudo: yes` and `sudo_user: postgres` in your play). 83 | 84 | **`postgresql_objects_ignore_revoke_failure` caveats**: If you typo a user, db, 85 | or table to revoke, this will happily indicate that revoking was successful 86 | (the named user does not have the listed privilege(s) on the provided db, after 87 | all) and continue on. This optional also only works if your roles parameter in 88 | postgresql_privileges only contains one role, since the entire revoke will fail 89 | and roll back if any of the listed roles don't exist, and that's a condition 90 | that we don't want to treat as successful. The PostgreSQL error string is 91 | checked for the text 'does not exist', so if your PostgreSQL server returns 92 | messages in a different locale, this feature probably won't work. 93 | 94 | Example Playbook 95 | ---------------- 96 | 97 | Set up a user for remote administration. The user gets a database so connecting 98 | as that user does not fail (the `postgresql_*` modules do not provide a 99 | mechanism to control what database to connect to as the login user): 100 | 101 | ```yaml 102 | - hosts: dbservers 103 | vars: 104 | postgresql_objects_users: 105 | - name: pgadmin 106 | password: shrubbery 107 | role_attr_flags: SUPERUSER 108 | postgresql_objects_databases: 109 | - name: pgadmin 110 | owner: pgadmin 111 | roles: 112 | - role: galaxyproject.postgresql_objects 113 | become: yes 114 | become_user: postgres 115 | ``` 116 | 117 | Create a passwordless user (for use with unix domain socket connections) with a 118 | database of the same name: 119 | 120 | ```yaml 121 | - hosts: dbservers 122 | vars: 123 | postgresql_objects_users: 124 | - name: foo 125 | postgresql_objects_databases: 126 | - name: foo 127 | owner: foo 128 | postgresql_objects_login_user: pgadmin 129 | postgresql_objects_login_password: shrubbery 130 | roles: 131 | - role: galaxyproject.postgresql_objects 132 | ``` 133 | 134 | Grant all privileges on table `bar` in database `foo` to new user `baz`, and 135 | SELECT privileges to `baz` on sequence `bar_quux_seq` in database `foo`: 136 | 137 | ```yaml 138 | - hosts: dbservers 139 | vars: 140 | postgresql_objects_users: 141 | - name: baz 142 | db: foo 143 | postgresql_objects_privileges: 144 | - database: foo 145 | roles: baz 146 | objs: bar_quux_seq 147 | type: sequence 148 | privs: SELECT 149 | roles: 150 | - role: galaxyproject.postgresql_objects 151 | become: yes 152 | become_user: postgres 153 | ``` 154 | 155 | Create a group `plugh` and add `foo` and `baz` to this group: 156 | 157 | ```yaml 158 | - hosts: dbservers 159 | vars: 160 | postgresql_objects_groups: 161 | - name: plugh 162 | users: 163 | - name: foo 164 | - name: baz 165 | roles: 166 | - role: galaxyproject.postgresql_objects 167 | become: yes 168 | become_user: postgres 169 | ``` 170 | 171 | Revoke specific privileges for user `foo`, remove user `baz` from group 172 | `plugh`, and delete user `baz`: 173 | 174 | ```yaml 175 | - hosts: dbservers 176 | vars: 177 | postgresql_objects_users: 178 | - name: baz 179 | state: absent 180 | postgresql_objects_groups: 181 | - name: plugh 182 | users: 183 | - name: baz 184 | state: absent 185 | postgresql_objects_privileges: 186 | - database: foo 187 | roles: foo 188 | objs: bar_quux_seq 189 | type: sequence 190 | privs: ALL 191 | state: absent 192 | - database: foo 193 | roles: baz 194 | objs: bar 195 | privs: ALL 196 | state: absent 197 | - database: foo 198 | roles: baz 199 | objs: bar_quux_seq 200 | type: sequence 201 | privs: ALL 202 | state: absent 203 | roles: 204 | - role: galaxyproject.postgresql_objects 205 | become: yes 206 | become_user: postgres 207 | ``` 208 | 209 | Dependencies 210 | ------------ 211 | 212 | None 213 | 214 | License 215 | ------- 216 | 217 | [MIT](https://opensource.org/licenses/MIT) 218 | 219 | Author Information 220 | ------------------ 221 | 222 | The [Galaxy Community](https://galaxyproject.org/) and [contributors](https://github.com/galaxyproject/ansible-postgresql-objects/graphs/contributors) 223 | -------------------------------------------------------------------------------- /defaults/main.yml: -------------------------------------------------------------------------------- 1 | --- 2 | # Revoking privileges from a deleted user, database, or table would normally 3 | # cause a failure with one of the following messages from Postgres: 4 | # 5 | # role "foo" does not exist 6 | # Could not connect to database: FATAL: database "foo" does not exist 7 | # relation "public.foo" does not exist 8 | # 9 | # If not handled, this would prevent this role from being used idempotently. 10 | # Caveat: if you typo a user, db, or table to revoke, this will happily 11 | # indicate that revoking was successful (the named user does not have the 12 | # listed privilege(s) on the provided db, after all) and continue on. 13 | # 14 | # This only works if your roles parameter in postgresql_privileges only 15 | # contains one role, since the entire revoke will fail and roll back if any of 16 | # the listed roles don't exist, and that's a condition that we don't want to 17 | # treat as successful. 18 | # 19 | # The error string is checked for the text 'does not exist', so if your 20 | # Postgres server returns messages in a different locale, this probably won't 21 | # work. 22 | postgresql_objects_ignore_revoke_failure: true 23 | 24 | postgresql_objects_users: [] 25 | postgresql_objects_groups: [] 26 | postgresql_objects_databases: [] 27 | postgresql_objects_privileges: [] 28 | -------------------------------------------------------------------------------- /meta/main.yml: -------------------------------------------------------------------------------- 1 | --- 2 | galaxy_info: 3 | namespace: galaxyproject 4 | role_name: postgresql_objects 5 | author: The Galaxy Project 6 | company: The Galaxy Project 7 | description: "Configure PostgreSQL objects: users (roles), databases, and privileges" 8 | license: MIT 9 | min_ansible_version: "2.4" 10 | platforms: 11 | - name: EL 12 | versions: 13 | - all 14 | - name: GenericUNIX 15 | versions: 16 | - all 17 | - name: Fedora 18 | versions: 19 | - all 20 | - name: opensuse 21 | versions: 22 | - all 23 | - name: Amazon 24 | versions: 25 | - all 26 | - name: GenericBSD 27 | versions: 28 | - all 29 | - name: FreeBSD 30 | versions: 31 | - all 32 | - name: Ubuntu 33 | versions: 34 | - all 35 | - name: SLES 36 | versions: 37 | - all 38 | - name: GenericLinux 39 | versions: 40 | - all 41 | - name: Debian 42 | versions: 43 | - all 44 | galaxy_tags: 45 | - database 46 | - postgres 47 | - postgresql 48 | dependencies: [] 49 | -------------------------------------------------------------------------------- /molecule/_common/Dockerfile.j2: -------------------------------------------------------------------------------- 1 | FROM {{ item.image }} 2 | 3 | RUN apt update -qq && DEBIAN_FRONTEND=noninteractive apt install --no-install-recommends -y python3 python3-psycopg2 4 | 5 | ENV POSTGRES_PASSWORD molecule 6 | -------------------------------------------------------------------------------- /molecule/_common/requirements.yml: -------------------------------------------------------------------------------- 1 | --- 2 | 3 | roles: [] 4 | collections: 5 | - name: community.postgresql 6 | version: ">=2.2.0,<3" 7 | -------------------------------------------------------------------------------- /molecule/create-destroy/converge.yml: -------------------------------------------------------------------------------- 1 | --- 2 | - name: Converge 3 | hosts: all 4 | become: true 5 | become_method: su 6 | become_user: postgres 7 | roles: 8 | 9 | # this is not idempotent 10 | 11 | # create some stuff 12 | - role: galaxyproject.postgresql_objects 13 | postgresql_objects_users: 14 | - name: alice 15 | - name: bob 16 | - name: carol 17 | postgresql_objects_groups: 18 | - name: friends 19 | users: 20 | - name: alice 21 | - name: bob 22 | - name: carol 23 | - name: pals 24 | users: 25 | - name: alice 26 | - name: bob 27 | postgresql_objects_databases: 28 | - name: alicedb 29 | owner: alice 30 | - name: nobodydb 31 | postgresql_objects_privileges: 32 | - database: alicedb 33 | roles: mallory 34 | type: database 35 | privs: ALL 36 | state: absent 37 | 38 | - role: galaxyproject.postgresql_objects 39 | postgresql_objects_users: 40 | - name: alice 41 | state: absent 42 | postgresql_objects_databases: 43 | - name: alicedb 44 | state: absent 45 | postgresql_objects_groups: 46 | - name: friends 47 | users: 48 | - name: carol 49 | state: absent 50 | - name: pals 51 | state: absent 52 | postgresql_objects_privileges: 53 | - database: alicedb 54 | roles: mallory 55 | type: database 56 | privs: ALL 57 | state: absent 58 | -------------------------------------------------------------------------------- /molecule/create-destroy/molecule.yml: -------------------------------------------------------------------------------- 1 | --- 2 | dependency: 3 | name: galaxy 4 | options: 5 | force: false # don't fetch every time 6 | role-file: molecule/_common/requirements.yml 7 | requirements-file: molecule/_common/requirements.yml 8 | driver: 9 | name: docker 10 | platforms: 11 | - name: postgresql-objects-scenario-create-destroy 12 | image: postgres:latest 13 | command: postgres 14 | dockerfile: ../_common/Dockerfile.j2 15 | pre_build_image: false 16 | provisioner: 17 | name: ansible 18 | verifier: 19 | name: ansible 20 | scenario: 21 | test_sequence: 22 | - dependency 23 | - lint 24 | - cleanup 25 | - destroy 26 | - syntax 27 | - create 28 | - prepare 29 | - converge 30 | - cleanup 31 | - destroy 32 | -------------------------------------------------------------------------------- /molecule/default/converge.yml: -------------------------------------------------------------------------------- 1 | --- 2 | - name: Converge 3 | hosts: all 4 | become: true 5 | become_method: su 6 | become_user: postgres 7 | roles: 8 | 9 | # ensure the role runs if no objects are specified 10 | - role: galaxyproject.postgresql_objects 11 | 12 | # create some stuff, must pass idempotence 13 | - role: galaxyproject.postgresql_objects 14 | postgresql_objects_users: 15 | - name: alice 16 | - name: bob 17 | - name: carol 18 | password: hunter2 19 | - name: dan 20 | - name: eve 21 | state: absent 22 | - name: mallory 23 | postgresql_objects_groups: 24 | - name: friends 25 | users: 26 | - name: alice 27 | - name: bob 28 | - name: carol 29 | - name: dan 30 | state: absent 31 | - name: enemies 32 | users: 33 | - name: mallory 34 | - name: ghosts 35 | state: absent 36 | postgresql_objects_databases: 37 | - name: alicedb 38 | owner: alice 39 | - name: sharedb 40 | owner: friends 41 | - name: ghostdb 42 | state: absent 43 | postgresql_objects_privileges: 44 | - database: alicedb 45 | roles: bob 46 | objs: ALL_IN_SCHEMA 47 | type: sequence 48 | privs: SELECT 49 | -------------------------------------------------------------------------------- /molecule/default/molecule.yml: -------------------------------------------------------------------------------- 1 | --- 2 | dependency: 3 | name: galaxy 4 | options: 5 | force: false # don't fetch every time 6 | role-file: molecule/_common/requirements.yml 7 | requirements-file: molecule/_common/requirements.yml 8 | driver: 9 | name: docker 10 | platforms: 11 | - name: postgresql-objects-scenario-default 12 | image: postgres:latest 13 | command: postgres 14 | dockerfile: ../_common/Dockerfile.j2 15 | pre_build_image: false 16 | provisioner: 17 | name: ansible 18 | verifier: 19 | name: ansible 20 | -------------------------------------------------------------------------------- /molecule/default/verify.yml: -------------------------------------------------------------------------------- 1 | --- 2 | 3 | - name: Verify 4 | hosts: all 5 | become: true 6 | become_method: su 7 | become_user: postgres 8 | tasks: 9 | - block: 10 | 11 | - name: Check users and groups 12 | community.postgresql.postgresql_user: 13 | name: "{{ item.name }}" 14 | state: "{{ item.state | default('present') }}" 15 | loop: 16 | - name: alice 17 | - name: bob 18 | - name: carol 19 | - name: dan 20 | - name: eve 21 | state: absent 22 | - name: mallory 23 | - name: friends 24 | - name: enemies 25 | - name: ghosts 26 | state: absent 27 | register: result 28 | failed_when: result is changed 29 | 30 | - name: Check group membership 31 | community.postgresql.postgresql_membership: 32 | group: "{{ item.name }}" 33 | target_roles: "{{ item.users }}" 34 | state: "{{ item.state | default('present') }}" 35 | loop: 36 | - name: friends 37 | users: 38 | - alice 39 | - bob 40 | - carol 41 | - name: friends 42 | state: absent 43 | users: 44 | - dan 45 | - mallory 46 | - name: enemies 47 | users: 48 | - mallory 49 | register: result 50 | failed_when: result is changed 51 | 52 | - name: Check databases 53 | community.postgresql.postgresql_db: 54 | name: "{{ item.name }}" 55 | owner: "{{ item.owner | default(omit) }}" 56 | state: "{{ item.state | default('present') }}" 57 | loop: 58 | - name: alicedb 59 | owner: alice 60 | - name: sharedb 61 | owner: friends 62 | - name: ghostdb 63 | state: absent 64 | register: result 65 | failed_when: result is changed 66 | 67 | - name: Check extra privs 68 | community.postgresql.postgresql_privs: 69 | database: "{{ item.database }}" 70 | roles: "{{ item.roles }}" 71 | objs: "{{ item.objs }}" 72 | type: "{{ item.type }}" 73 | privs: "{{ item.privs }}" 74 | state: "{{ item.state | default('present') }}" 75 | loop: 76 | - database: alicedb 77 | roles: bob 78 | objs: ALL_IN_SCHEMA 79 | type: sequence 80 | privs: SELECT 81 | register: result 82 | failed_when: result is changed 83 | 84 | check_mode: true 85 | -------------------------------------------------------------------------------- /tasks/main.yml: -------------------------------------------------------------------------------- 1 | --- 2 | 3 | # Fail is used here to make the message stand out (red) 4 | - name: Warn if deprecated user priv param is set 5 | ansible.builtin.fail: 6 | msg: "Support for the deprecated `priv` param to postgresql_objects_users has been removed, please use postgresql_objects_privileges instead" 7 | loop: "{{ postgresql_objects_users }}" 8 | loop_control: 9 | label: "{{ item.name }}" 10 | when: item.priv is defined 11 | ignore_errors: true 12 | 13 | - name: Revoke extra privileges 14 | community.postgresql.postgresql_privs: 15 | database: "{{ item.database }}" 16 | roles: "{{ item.roles }}" 17 | type: "{{ item.type | default(omit) }}" 18 | objs: "{{ item.objs | default(omit) }}" 19 | privs: "{{ item.privs | default(omit) }}" 20 | schema: "{{ item.schema | default(omit) }}" 21 | grant_option: "{{ item.grant_option | default(omit) }}" 22 | state: "{{ item.state | default(omit) }}" 23 | login_host: "{{ postgresql_objects_login_host | default(omit) }}" 24 | login_user: "{{ postgresql_objects_login_user | default(omit) }}" 25 | login_password: "{{ postgresql_objects_login_password | default(omit) }}" 26 | port: "{{ postgresql_objects_port | default(omit) }}" 27 | loop: "{{ postgresql_objects_privileges }}" 28 | register: revoke 29 | failed_when: >- 30 | revoke is failed and ( 31 | not postgresql_objects_ignore_revoke_failure or ( 32 | postgresql_objects_ignore_revoke_failure and ( 33 | ('does not exist' not in revoke.msg) or (',' in item.roles) 34 | ) 35 | ) 36 | ) 37 | when: (item.state | default('present')) == 'absent' 38 | 39 | # Drop databases first so later user drop can succeed 40 | - name: Drop databases 41 | community.postgresql.postgresql_db: 42 | name: "{{ item.name }}" 43 | owner: "{{ item.owner | default(omit) }}" 44 | template: "{{ item.template | default(omit) }}" 45 | encoding: "{{ item.encoding | default(omit) }}" 46 | lc_collate: "{{ item.lc_collate | default(omit) }}" 47 | lc_ctype: "{{ item.lc_ctype | default(omit) }}" 48 | state: "{{ item.state }}" 49 | login_host: "{{ postgresql_objects_login_host | default(omit) }}" 50 | login_user: "{{ postgresql_objects_login_user | default(omit) }}" 51 | login_password: "{{ postgresql_objects_login_password | default(omit) }}" 52 | port: "{{ postgresql_objects_port | default(omit) }}" 53 | loop: "{{ postgresql_objects_databases }}" 54 | when: (item.state | default('present')) == 'absent' 55 | 56 | - name: Create and drop users 57 | community.postgresql.postgresql_user: 58 | name: "{{ item.name }}" 59 | password: "{{ item.password | default(omit) }}" 60 | role_attr_flags: "{{ item.role_attr_flags | default(omit) }}" 61 | encrypted: "{{ item.encrypted | default(omit) }}" 62 | state: "{{ item.state | default(omit) }}" 63 | login_host: "{{ postgresql_objects_login_host | default(omit) }}" 64 | login_user: "{{ postgresql_objects_login_user | default(omit) }}" 65 | login_password: "{{ postgresql_objects_login_password | default(omit) }}" 66 | port: "{{ postgresql_objects_port | default(omit) }}" 67 | loop: "{{ postgresql_objects_users }}" 68 | loop_control: 69 | label: "{{ item.name }}" 70 | 71 | - name: Create groups 72 | community.postgresql.postgresql_user: 73 | name: "{{ item.name }}" 74 | password: "{{ item.password | default(omit) }}" 75 | role_attr_flags: "{{ item.role_attr_flags | default(omit) }}" 76 | encrypted: "{{ item.encrypted | default(omit) }}" 77 | login_host: "{{ postgresql_objects_login_host | default(omit) }}" 78 | login_user: "{{ postgresql_objects_login_user | default(omit) }}" 79 | login_password: "{{ postgresql_objects_login_password | default(omit) }}" 80 | port: "{{ postgresql_objects_port | default(omit) }}" 81 | loop: "{{ postgresql_objects_groups }}" 82 | when: (item.state | default('present')) == 'present' 83 | loop_control: 84 | label: "{{ item.name }}" 85 | 86 | # this could be done more efficiently if we changed the data structure in a backwards-incompatible way 87 | - name: Add or remove users from groups 88 | community.postgresql.postgresql_membership: 89 | group: "{{ item.0.name }}" 90 | target_role: "{{ item.1.name }}" 91 | state: "{{ item.1.state | default('present') }}" 92 | login_host: "{{ postgresql_objects_login_host | default(omit) }}" 93 | login_user: "{{ postgresql_objects_login_user | default(omit) }}" 94 | login_password: "{{ postgresql_objects_login_password | default(omit) }}" 95 | port: "{{ postgresql_objects_port | default(omit) }}" 96 | loop: "{{ postgresql_objects_groups | subelements('users', skip_missing=True) }}" 97 | when: (item.0.state | default('present')) == 'present' 98 | loop_control: 99 | label: "{{ item.0.name }}: {{ item.1.name }}" 100 | 101 | - name: Drop groups 102 | community.postgresql.postgresql_user: 103 | name: "{{ item.name }}" 104 | state: "{{ item.state }}" 105 | login_host: "{{ postgresql_objects_login_host | default(omit) }}" 106 | login_user: "{{ postgresql_objects_login_user | default(omit) }}" 107 | login_password: "{{ postgresql_objects_login_password | default(omit) }}" 108 | port: "{{ postgresql_objects_port | default(omit) }}" 109 | loop: "{{ postgresql_objects_groups }}" 110 | when: (item.state | default('present')) == 'absent' 111 | loop_control: 112 | label: "{{ item.name }}" 113 | 114 | - name: Create databases 115 | community.postgresql.postgresql_db: 116 | name: "{{ item.name }}" 117 | owner: "{{ item.owner | default(omit) }}" 118 | template: "{{ item.template | default(omit) }}" 119 | encoding: "{{ item.encoding | default(omit) }}" 120 | lc_collate: "{{ item.lc_collate | default(omit) }}" 121 | lc_ctype: "{{ item.lc_ctype | default(omit) }}" 122 | state: "{{ item.state | default(omit) }}" 123 | login_host: "{{ postgresql_objects_login_host | default(omit) }}" 124 | login_user: "{{ postgresql_objects_login_user | default(omit) }}" 125 | login_password: "{{ postgresql_objects_login_password | default(omit) }}" 126 | port: "{{ postgresql_objects_port | default(omit) }}" 127 | loop: "{{ postgresql_objects_databases }}" 128 | when: (item.state | default('present')) == 'present' 129 | loop_control: 130 | label: "{{ item.name }}" 131 | 132 | - name: Grant extra privileges 133 | community.postgresql.postgresql_privs: 134 | database: "{{ item.database | default(omit) }}" 135 | roles: "{{ item.roles }}" 136 | type: "{{ item.type | default(omit) }}" 137 | objs: "{{ item.objs | default(omit) }}" 138 | privs: "{{ item.privs | default(omit) }}" 139 | schema: "{{ item.schema | default(omit) }}" 140 | grant_option: "{{ item.grant_option | default(omit) }}" 141 | state: "{{ item.state | default(omit) }}" 142 | login_host: "{{ postgresql_objects_login_host | default(omit) }}" 143 | login_user: "{{ postgresql_objects_login_user | default(omit) }}" 144 | login_password: "{{ postgresql_objects_login_password | default(omit) }}" 145 | port: "{{ postgresql_objects_port | default(omit) }}" 146 | loop: "{{ postgresql_objects_privileges }}" 147 | when: (item.state | default('present')) == 'present' 148 | loop_control: 149 | label: "{{ item.roles }}" 150 | --------------------------------------------------------------------------------