Compare commits

...

130 Commits

Author SHA1 Message Date
Varun Mylaraiah
b861a61857 Merge pull request #1073 from t-woerner/ipaserver_do_not_enable_RSN_by_default
ipaserver: Do not enable random serial numbers by default
2023-04-05 15:57:53 +05:30
Thomas Woerner
6faff2ac11 ipaserver: Do not enable random serial numbers by default
ipaserver_random_serial_numbers was enabled by default in
roles/ipaserver/defaults/main.yml. This should not be the default and
also resulted in issues in all IPA versions that do not support RSN.

The parameter now defaults to false.
2023-04-05 11:53:28 +02:00
Rafael Guterres Jeffman
82c0161245 Merge pull request #1072 from t-woerner/external_group_ipaexternalmember_fix
ipagroup: Fix ensuring external group group members (without trust-ad)
2023-04-04 17:56:11 -03:00
Thomas Woerner
ecab42b9f5 Merge pull request #1060 from rjeffman/ipaserver_random_serial_numbers
roles/ipaserver: Allow deployments with random serial numbers
2023-04-04 16:12:15 +02:00
Thomas Woerner
183ea7fd79 Merge pull request #1047 from dkarpele/dkarpele-1040
Update `EXAMPLE` sections for multiuser and multihost handling.
2023-04-04 16:00:21 +02:00
Rafael Guterres Jeffman
a4087a755b roles/ipaserver: Allow deployments with random serial numbers
Since FreeIPA version 4.10 it is possible to deploy servers that use
Random Serial Number v3 support for certificates.

This patch exposes the 'random_serial_numbers' parameter, as
'ipaserver_random_serial_numbers', allowing a user to have random serial
numbers enabled for the domain.

The use of random serial numbers is allowed on new installations only.
2023-04-04 10:35:07 -03:00
Thomas Woerner
fb3ff6d63d Merge pull request #1001 from dkarpele/dkarpele-879
[RFE] Allow multiple groups creation
2023-04-04 13:35:24 +02:00
Thomas Woerner
ee92d99243 ipagroup: Handle ensuring groups with mixed types without IPA fix 6741
Ensuring (adding) several groups with mixed types external, nonposix
and posix require to have a fix in IPA:

    FreeIPA issue: https://pagure.io/freeipa/issue/9349
    FreeIPA fix: https://github.com/freeipa/freeipa/pull/6741

The simple solution is to switch to client context for ensuring several
groups simply if the user was not explicitly asking for the server context
no matter if mixed types are used.
2023-04-04 13:13:41 +02:00
Denis Karpelevich
a649a8dfe1 [RFE] Allow multiple groups creation.
Adding an option `groups` to create multiple groups in one operation.
Adding tests (present/absent/external/nonposix) with server and
client context.
Simple example of `groups` option:
```
tasks:
- name: Ensure 2 groups are present
  ipagroup:
    ipaadmin_password: SomeADMINpassword
    groups:
    - name: group1
    - name: group2
```

Signed-off-by: Denis Karpelevich <dkarpele@redhat.com>
2023-04-04 13:13:40 +02:00
Thomas Woerner
80abf635c3 ipagroup: Fix ensuring external group group members (without trust-ad)
Due to an API misbehaviour in FreeIPA, ipaexternalmembers need to be
treated differently than other group members parameters. Even an empty
array triggers all tests for external members, including the check for
installed dcerpc bindings.

Therefore ipagroup module has been changed to not set ipaexternalmember
to an empty list if there are no external members to be added or
removed.
2023-04-03 15:00:47 +02:00
Rafael Guterres Jeffman
24e05d1df4 Merge pull request #1067 from t-woerner/ipaclient_ipaclient_defer_krb5_configuration_fix
ipaclient: Defer krb5 configuration fix
2023-03-30 16:32:16 -03:00
Rafael Guterres Jeffman
065e902182 Merge pull request #1068 from t-woerner/replica_server_uninstall_cleanup
ipareplica/server: Enable removal from domain with undeployment
2023-03-30 16:31:34 -03:00
Rafael Guterres Jeffman
96f5f5c86e Merge pull request #1069 from t-woerner/ansible_lint_fixes
Ansible lint fixes
2023-03-30 16:30:23 -03:00
Thomas Woerner
476d9d5057 ipareplica/server: Enable removal from domain with undeployment
New variables have been added to ipareplica and ipaserver role to enable
the removal from the domein with the undeployment.

`ipaserver_remove_from_domain`
This enables the removal of the server from the domain additionally to the
undeployment.

`ipaserver_remove_on_server`
The value defines the server/replica in the domain that will to be used to
remove the server/replica from the domain if
`ipaserver_ignore_topology_disconnect` and `ipaserver_remove_from_domain`
are enabled. Without the need to enable
`ipaserver_ignore_topology_disconnect`, the value will be automatically
detected using the replication agreements of the server/replica.

For the replica role it is possible to use the server variables, but
also the replica versions: `ipareplica_remove_from_domain` and
`ipareplica_remove_on_server`.

The already existing parameters `ipaserver_ignore_topology_disconnect` and
`ipaserver_ignore_last_of_role` have been added to the README files for
server and replica with descriptions. The same for the replica versions
of the parameters.

The ipareplica role is not calling the `ipa-server-install` anymore, it
is instead using (including) the server role for the task.

The new module `ipaserver_get_connected_server` has been added to the
server role to be able to get a connected server using the replication
agreements. This module is only used if
`ipaserver_ignore_topology_disconnect` is not needed.
2023-03-28 10:29:07 +02:00
Thomas Woerner
049024bbb2 tests/config/test_config_sid: Mark tasks as noqa 503
The latest ansible-lint failes for the tasks that are using
"when: sid_disabled.changed" with the error
"Tasks that run when changed should likely be handlers.". As
these tasks are tests and it would not make sense to use handlers here,
the tasks have been marked as noqa 503.
2023-03-27 12:29:30 +02:00
Thomas Woerner
ec03ad2bf9 ipareplica/server: Always cleanup root IPA cache
The cleanup of the root IPA cache was depending on the result of the
ipaserver_enable_ipa and ipareplica_enable_ipa tasks. Instead of
"when: something.changed" a handler should be used instead. As
"/root/.ipa_cache" should be removed always (same in command line) the
removal of the file has been moded into the always section and does not
need a when anymore.
2023-03-27 12:24:02 +02:00
Thomas Woerner
64c43c1ec0 ipaclient_configure_dns_resolver: Removed bad aliases
The parameters nameservers and searchdomains had both the alias "cn".
Both aliases have been removed.
2023-03-27 12:21:37 +02:00
Thomas Woerner
b1eb32993d ipapwpolicy: The alias for usercheck in argument_spec had typo
The alias for usercheck in argument_spec was "ipapwusercheck" instead of
"ipapwdusercheck".
2023-03-27 12:20:14 +02:00
Thomas Woerner
2ee7139560 ipanetgroup: Missing type for action and state DOCUMENTATION section
The types for the parameters action and state have been missing in the
DOCUMENTATION section of the module.
2023-03-27 12:17:38 +02:00
Thomas Woerner
10d072a8c4 ipaclient: ipaclient_fix_ca also needs krb_name parameter
With the fix to defer creating the final krb5.conf on clients a bug has
been introduced with ipaclient_fix_ca: The krb_name parameter that
points to the temporary krb5 configuration was not added to the module

Without this the server affinity is broken for allow_repair and additionally
ipaclient_fix_ca could fail if krb5 configuration needs to be repraied
and also CA needs to be fixed.

The krb_name parameter has been added to ipaclient_fix_ca and is also
properly set in tasks/install.yml.
2023-03-24 12:51:59 +01:00
Thomas Woerner
0ec89eb53c ipaclient: ipaclient_setup_nss also needs krb_name parameter
With the fix to defer creating the final krb5.conf on clients a bug has
been introduced with ipaclient_setup_nss: The krb_name parameter that
points to the temporary krb5 configuration was not added to the module.

With a properly configured DNS (like for example IPA DNS) the krb TXT
records have been present in the DNS configuration. These have been used
automatically as a fallback and broke server affinity for the client.
Without the TXT records creating the IPA NSS database failed with
 "Cannot find KDC for realm ..".

The krb_name parameter has been added to ipaclient_setup_nss and is also
properly set in tasks/install.yml.
2023-03-24 12:37:48 +01:00
Thomas Woerner
cf27a98c61 Merge pull request #1045 from rjeffman/ipauser_param_description
ipauser: Better description of UID and GID parameters
2023-03-20 14:09:39 +01:00
Thomas Woerner
fd3e87771a Merge pull request #1062 from rjeffman/ipareplica_remove_undefined_params
ipareplica role: Remove usage of undefined parameters.
2023-03-20 13:42:30 +01:00
Rafael Guterres Jeffman
e03752955f ipareplica role: Remove usage of undefined parameters.
Some ipareplica role had a few module calls with parameters set like
'some_argument | default(omit)' that were not actually available in such
modules. If a user provided 'some_argument', the paramater would then
be passed to the module and ipareplica deployment would fail.

By removing the parameters from the 'install' task, ipareplica
deployment works even if the variables are set by the user.
2023-03-16 22:28:29 -03:00
Rafael Guterres Jeffman
338df6e60e Merge pull request #1058 from t-woerner/ipahost_make_return_value_depending_on_hosts_param
ipahost: Make return value depending on hosts parameter
2023-03-16 10:10:26 -03:00
Thomas Woerner
3f3e495ab3 ipahost: Make return value depending on hosts parameter
The way how randompasswords are returned by the ipahost module depends
so far on the number of hosts that are handled by the module.

This is unexpected if for example a json file is provided with the hosts
parameter. As it might be unknown how many hosts are in the json file,
this behaviour is unexpected. The return should not vary in this case.

This chamge makes the return simply depend on the use of the hosts
paramater. As soon as this parameter is used, the return will always be:

"host": { "<the host>": { "randompassword": "<the host random password>" } }

In the simply case with one host it will be still

"host": { "randompassword": "<the host random password>" }

This change for ipahost is related to the ipauser PR #1053.
2023-03-14 12:56:33 +01:00
Rafael Guterres Jeffman
b05aec98c5 Merge pull request #1053 from t-woerner/ipauer_make_return_value_depending_on_users_parameter
ipauser: Make return value depending on users parameter
2023-03-10 08:26:35 -03:00
Rafael Guterres Jeffman
867f7ed520 Merge pull request #1050 from t-woerner/ipaclient_defer_krb5_configuration
ipaclient: Defer creating the final krb5.conf on clients
2023-03-09 18:05:42 -03:00
Thomas Woerner
3cc17a43aa Merge pull request #974 from dkarpele/dkarpele-919
Add subid option to select the sssd profile with-subid.
2023-03-08 13:56:48 +01:00
Denis Karpelevich
2b0b7db086 Add subid option to select the sssd profile with-subid.
This is an ansible-freeipa update for the freeipa RFE:
https://pagure.io/freeipa/issue/9159
"`ipa-client-install` should provide option to enable `subid: sss`
in `/etc/nsswitch.conf`".

This option allows to configure authselect with the sssd
profile + with-subid feature, in order to have SSSD setup as
a datasource for subid in /etc/nsswitch.conf.

The default behavior remains unchanged: without the option,
/etc/nsswitch.conf keeps the line subid: files

Signed-off-by: Denis Karpelevich <dkarpele@redhat.com>
2023-03-06 16:06:33 +01:00
Thomas Woerner
87afc56ee6 Merge pull request #1051 from rjeffman/fedora-spdx
Migrated to SPDX license.
2023-03-02 13:55:13 +01:00
Thomas Woerner
61caa57801 ipauser: Make return value depending on users parameter
The way how randompasswords are returned by the ipauser module depends
so far on the number of users that are handled by the module.

This is unexpected if for example a json file is provided with the users
parameter. As it might be unknown how many users are in the json file,
this behaviour is unexpected. The return should not vary in this case.

This chamge makes the return simply depend on the use of the users
paramater. As soon as this parameter is used, the return will always be:

"user": { "<the user>": { "randompassword": "<the user random password>" } }

In the simply case with one user it will be still

"user": { "randompassword": "<the user random password>" }

Fixes: #1052 (ipauser should consitently return randompasswords when
              used with users)
2023-03-02 11:42:32 +01:00
Thomas Woerner
6b5acd9b0c ipaclient: Defer creating the final krb5.conf on clients
A temporary krb5 configuration was used to join the domain in
ipaclient_join. After that the final krkb5 configuration was created
with enabled DNS discovery and used for the remainaing tasks, where also
a connection to the IPA API was done.

With several servers the DNS discovery could have picked up a different
server. If the client deployment was faster than the replication this
could have lead to an unknown host error.

The issue was seen in performance testing where many simultaneous client
enrollments have been done..

The goal is to keep server affinity as long as possible within the
deployment process:

The temporary krb5.conf that was used before in ipaclient_join was
pulled out into an own module. The generated temporary krb5.conf is now
used in ipaclient_join and also ipaclient_api.

The generation of the final krb5.conf is moved to the end of the
deployment process.

Same as: https://pagure.io/freeipa/issue/9228

The setup of certmonger has been pulled out of ipaclient_setup_nss and moved
to the end of the process after generating the final krb5.conf as it will
use t will only use /etc/krb5.conf.

Certificate issuance may fail during deployment due to using the final
krb5.conf, but certmonger will re-try the request in this case.

Same as: https://pagure.io/freeipa/issue/9246
2023-02-27 16:09:34 +01:00
Denis Karpelevich
78b5e66da4 Update EXAMPLE sections for multiuser and multihost handling.
Signed-off-by: Denis Karpelevich <dkarpele@redhat.com>
2023-02-23 21:53:03 +01:00
Rafael Guterres Jeffman
f6c376a68f Migrated to SPDX license.
According to [1] all Fedora packages need to be updated to use a SPDX
expression. This patch updates the ansible-freeipa spec template to
comply with this change.

[1] https://fedoraproject.org/wiki/Changes/SPDX_Licenses_Phase_1
2023-02-23 17:27:33 -03:00
Rafael Guterres Jeffman
691fbd083e ipauser: Better description of UID and GID parameters
This patch provides better text for the description of UID and GID
parameters.
2023-02-23 14:50:11 -03:00
Thomas Woerner
77cd20bc10 Merge pull request #1046 from rjeffman/fix_ansible_lint_tests
Fix ansible-lint on tests
2023-02-22 14:24:37 +01:00
Rafael Guterres Jeffman
16ce5f21de ansible-lint: License must be defined as a list. 2023-02-21 11:26:29 -03:00
Rafael Guterres Jeffman
dcf9c7d8ce ansible-lint: Fixed dangling 'when' clause.
A dangling 'when:' clause was failing anisble-lint tests as the task did
not match any valid schema. The dangling clause was removed, and the
usage of 'shell' was changed from free form to use the 'cmd' parameter.
2023-02-21 11:26:29 -03:00
Rafael Guterres Jeffman
c715d3aad2 ansible-lint: Fix key order on upstream tests
In latest ansible-lint versions, the use of "blocks" has a required
order to be implemented. According to ansible-lint error mesage, the
order is name, when, block, rescue, always.

As not following this rule is now an error, this patch fixes all tests
for the 'key-order[task]' error.
2023-02-21 11:26:29 -03:00
Rafael Guterres Jeffman
0d1e9d3f49 ansible-lint: Use 'missing-import' instead of '505'
ansible-lint is issuing an warning when using '# noqa 505' instead of
'#noqa missing-import' on playbooks. This patch changes all occurrences
of the tag to use the newer format.
2023-02-21 11:26:29 -03:00
Rafael Guterres Jeffman
b30ae1c9b5 Merge pull request #1037 from t-woerner/fix_allow_repair_missing_krb5.conf_with_DNS_lookup
ipaclient: Fix allow_repair with removed krb5.conf and DNS lookup
2023-02-09 07:57:53 -03:00
Thomas Woerner
bfeefaf454 ipaclient: Fix allow_repair with removed krb5.conf and DNS lookup
The test in ipaclient_test_keytab is at first trying to use an existing
krb5.conf to test if the host keytab can be used. With working DNS lookup
an absent krb5.conf is not reported as an error as DNS lookup is
silently used instead.

A temporary krb5.conf is now used in this test that forces to deactivate
DNS lookups and also to load /etc/krb5.conf. A missing krb5.conf is now
detected properly as the kinit call fails now properly. Thanks to Julien
Rische for this proposal.

ipaclient_test_keytab is now properly returning the state of usable or
not usable krb5.conf in krb5_conf_ok. This fixes the handling of this
case later on in the role.
2023-02-08 16:14:38 +01:00
Rafael Guterres Jeffman
0c23ae5b37 Merge pull request #1033 from t-woerner/use_ipabackup_item_again
ipabackup: Use ipabackup_item again in copy_backup_to_server
2023-01-31 10:29:55 -03:00
Thomas Woerner
3b4367cf89 ipabackup: Use ipabackup_item again in copy_backup_to_server
ipabackup_item needs to be set again in copy_backup_to_server.yml. The
variable is later on used in restore.yml.
2023-01-31 10:16:53 +01:00
Thomas Woerner
e96f92c36f Merge pull request #1031 from rjeffman/ci_increase_timeout
upstream CI: increase Azure test timeout.
2023-01-23 20:07:38 +01:00
Rafael Guterres Jeffman
683a894876 upstream CI: increase Azure test timeout.
Due to DNS issues and the increase number of tests, the timeout setting
used for upstream tests was being reached. As we still have room for
running the tests using Azure infrastructure, this patch increases the
timeout to 240 minutes (4h), per worker.
2023-01-23 14:42:43 -03:00
Rafael Guterres Jeffman
2761c7e8d9 Merge pull request #1030 from t-woerner/use_yml_extension_for_pytest_tests
Use yml extension for pytest tests
2023-01-19 15:48:32 -03:00
Thomas Woerner
7d3921e510 Use yml extension for pytest tests
The pytest .yaml files have been rename to .yml to enable
build-galaxy-release to fix the prefix of the ansible-freeipa modules.
2023-01-19 16:07:42 +01:00
Thomas Woerner
6000aac687 Merge pull request #1029 from rjeffman/automount_tests_fix_ansible_lint
playbooks: Fix automount tasks to make ansible-lint happy
2023-01-18 09:45:17 +01:00
Thomas Woerner
e8354932b4 Merge pull request #1028 from rjeffman/dnszone_fix_typo
dnszone tests: Fix typo on task names.
2023-01-18 09:44:47 +01:00
Rafael Guterres Jeffman
a3089484b1 playbooks: Fix automount tasks to make ansible-lint happy
A few playbooks still had task name starting with lower case letters.
2023-01-17 14:24:09 -03:00
Rafael Guterres Jeffman
1469ac6058 dnszone tests: Fix typo on task names. 2023-01-17 14:00:28 -03:00
Rafael Guterres Jeffman
308d970b6c Merge pull request #1026 from t-woerner/ansible_lint_tests
Ansible lint tests
2023-01-17 12:02:50 -03:00
Rafael Guterres Jeffman
7b470ceb60 Merge pull request #1022 from t-woerner/pwpolicy_bool_checks
pwpolicy: Fix new bool checks for IPA prior to 4.9.10
2023-01-17 11:49:23 -03:00
Rafael Guterres Jeffman
77f5d8751f Merge pull request #1027 from t-woerner/use_yml_extension_for_all_automount_example_playbooks
playbooks/automount: All playbooks should use .yml
2023-01-17 09:45:17 -03:00
Thomas Woerner
3292252802 playbooks/automount: All playbooks should use .yml
The playbooks automount-map-absent.yaml and automount-map-present.yaml
have been using the wrong extention. The files have been renamed to use
.yml now.
2023-01-17 13:34:19 +01:00
Thomas Woerner
414dc06c86 ansible-lint: All names should start with an uppercase letter 2023-01-17 12:53:02 +01:00
Thomas Woerner
d2f9fe6325 Fix jinja2 white spaces issues reported by ansible-lint
This replaces double spaces by single spaces, fixes spaces in slices,
adds spaces before brackets and fixes bracket placing in when clauses.
2023-01-17 12:38:51 +01:00
Thomas Woerner
d7c02d1347 Improve jinja2 spacing: Remove space between join and ()
This change removes the space between join and (): "join ()" to "join()"
2023-01-17 11:51:38 +01:00
Thomas Woerner
cc6a80fa88 .github/workflows/lint.yml: Enable ansible-lint for the whole collection
The whole collection is tested with this change. Before it has been
limited to the roles and plugins folder.
2023-01-17 11:28:27 +01:00
Thomas Woerner
fe6edbabdb .ansible-lint: Deactivate experimental and name[template] tests
The experimental tests is running several additional tests like for
example to check module arg values. It fails everytime a variable is
used to pass the value in.

Examples:
- playbooks/topology/add-topologysegments.yml:15: args[module]: value of
  suffix must be one of: domain, ca, domain+ca, got: {{ item.suffix }}
- tests/host/test_host.yml:21: args[module]: value of ipaapi_context must
  be one of: server, client, got: {{ ipa_context | default(omit) }}

The name template test is failing for every template use inside of a name.
This is forcing to have only generic names and nothing specific in the
log anymore.

These two tests have been deactivated to have less overflow in the
ansible-lint output.
2023-01-17 11:20:10 +01:00
Rafael Guterres Jeffman
434905432d Merge pull request #1024 from rjeffman/pwpolicy_client_fix
pwpolicy: Fix tests for 'minlength: ""'
2023-01-16 22:44:51 -03:00
Rafael Guterres Jeffman
9f773ff5ac pwpolicy: Fix tests for 'minlength: ""'
When clearing minimum length parameter, FreeIPA raises an error, and the
error is different when executing the playbook in server or client
context. Since the error message is evaluated in the text, both errors
must be accepted as "not a failure", since ansible-freeipa did the
correct call.

Once https://pagure.io/freeipa/issue/9297 is fixed, the test must be
updated to not accept any of these error messages.
2023-01-16 21:33:46 -03:00
Rafael Guterres Jeffman
e95bec1803 Merge pull request #1023 from t-woerner/ansible_lint_needs_collection_source_dir
.github/workflows/lint.yml: ansible-lint needs collection source dir
2023-01-16 16:47:24 -03:00
Thomas Woerner
ea709ebc4d .github/workflows/lint.yml: ansible-lint needs collection source dir
ansible-lint required to be run in a collection source directory with
correct and working galaxy.yml

As ansible-freeipa is not converted to a collection, the galaxy.yml file
can not be used to create the collection. This needs to be done with
utils/build-galaxy-release.sh. The script is fixing all the prefixes for
the roles and modules in all the yml files and also example snippets and
in the documentation.

Therefore utils/build-galaxy-release.sh is called with the "-k" option
to keep the directory that has been used to generate the collection with
the script. Afterwards ansible-lint is run in this build directory.
2023-01-16 16:45:36 -03:00
Thomas Woerner
add89c25ee Merge pull request #1014 from rjeffman/roles_ansible_lint
Fix ansible-test lint warnings in roles.
2023-01-16 18:20:49 +01:00
Thomas Woerner
9108065ea7 pwpolicy: Fix new bool checks for IPA prior to 4.9.10
With 4.9.10, the value of bools have been changed from "TRUE" and
"FALSE" to real bool values.

With IPA < 4.9.10 the new bool checks distcheck and usercheck failed
the tests for enabling the checks with a "already enabled" error.

A new version check altogether with providing the ansible module for
gen_args has been added. The values True and False are now transformed
into "TRUE" and "FALSE" for IPA < 4.9.10.

The function bool_param has been renamed to bool_or_empty_param to match
the int_or_empty_param and to have a more explaining name.
2023-01-16 16:35:02 +01:00
Thomas Woerner
6cac891287 Merge pull request #977 from rjeffman/ci_update_ansible_2_14
upstream ci: Update Ansible versions on Azure pipelines.
2023-01-13 16:28:22 +01:00
Thomas Woerner
fc5fc9d9ef Merge pull request #1012 from rjeffman/pwpolicy_clean_values
pwpolicy: Allow clearing policy values.
2023-01-13 15:00:25 +01:00
Thomas Woerner
670740bdc0 Merge pull request #999 from rjeffman/update_tool_versions
Update development and Github workflow tools.
2023-01-13 14:53:49 +01:00
Rafael Guterres Jeffman
529deae407 ansible-lint: Fix file kind and ignores.
ansible-lint must ignore Azure configuration, and handle non-test files
with the proper kind (tasks or playbook).
2023-01-12 13:21:52 -03:00
Rafael Guterres Jeffman
a945862540 roles: Fix ansible-lint name:template warnings
ansible-lint warns if Jinja2 templates are not used as the last item in
a task name.
2023-01-12 13:13:31 -03:00
Rafael Guterres Jeffman
8240d9beb6 roles: Fix ansible-lint warning on var-naming.
ansible-lint warns if set_fact sets a variable where the name is used
or can be as a parameter for the role.
2023-01-12 12:49:41 -03:00
Rafael Guterres Jeffman
6da6110432 Fix issues raised by Flake8 version 5.0.3 2023-01-12 12:34:28 -03:00
Rafael Guterres Jeffman
1d8deb8e2d Fix issues raised by Pylint version 2.14.4. 2023-01-12 12:34:28 -03:00
Rafael Guterres Jeffman
b3856a1e2c Update Github workflow linter and check tools.
Update Github workflow tools to match the versions on Fedora 37.
2023-01-12 12:34:28 -03:00
Rafael Guterres Jeffman
410682a01d pwpolicy: Allow clearing policy values.
All values for pwpolicy can be cleared with an empty string in IPA CLI,
and this behavior was missing in ansible-freeipa.

As of today, there is an issue in FreeIPA that does not allow clearing
'minlength' policy. The is is tracked by the FreeIPA project through
https://pagure.io/freeipa/issue/9297

Fixes https://bugzilla.redhat.com/show_bug.cgi?id=2150334
2023-01-12 12:18:57 -03:00
Rafael Guterres Jeffman
ee59ec2142 upstream ci: Update Ansible versions on Azure pipelines.
As we now have ansible-core 2.14 available through 'pip', the versions
used for testing on Azure should be 2.12, 2.13 and 2.14, as Ansible
keeps upstream support for the latest version plus the two previous
ones.

This patch update the version used in tests by increasing the version
used by 1 (MINOR).
2023-01-11 19:00:13 -03:00
Rafael Guterres Jeffman
d043a3bdd1 Update development tools.
Update development tools to match Fedora 37 versions.
2023-01-11 18:38:37 -03:00
Rafael Guterres Jeffman
5062ac2b09 roles: Fix when, block and always key order.
ansible-lint warns if  'warn' key is used before block and always keys.
2023-01-11 14:37:39 -03:00
Rafael Guterres Jeffman
292e2eb60e roles: Fix jinja2 template spacing
This patch fixes ansible-lint warns on jinja2 template spacing in roles
2023-01-11 14:29:40 -03:00
Rafael Guterres Jeffman
baa7cae8bf roles: Fix task names to start with uppercase letters
ansible-lint warns if task names don't start with an uppercase letter.
2023-01-11 14:29:33 -03:00
Rafael Guterres Jeffman
6b7633976c roles: Fix use of ansible.builtin.fail free-form message.
ansible-lint warns to avoid using free-form when calling module actions
and ansible-freeipa roles used this form with 'ansible.builtin.fail'.
2023-01-11 14:27:59 -03:00
Rafael Guterres Jeffman
9a32359a5d roles: Fix type of data used for for versions in meta files
ansible-lint warns if version strings are used as numbers instead fo
strings.
2023-01-11 14:27:59 -03:00
Rafael Guterres Jeffman
82e176af95 Merge pull request #1013 from t-woerner/unnamed-tasks
yamllint: All tasks need to be named
2023-01-11 12:08:29 -03:00
Thomas Woerner
2a1ecdbd83 yamllint: All tasks need to be named
yamllint is failing for unnamed tasks. All block and include_tasks tasks
are now named.
2023-01-11 15:27:35 +01:00
Rafael Guterres Jeffman
f8b5851610 Merge pull request #1016 from t-woerner/galaxyfy_ansible_builtin
utils files: Support builtins with ansible.builtin. prefix
2023-01-09 17:54:23 -03:00
Thomas Woerner
b760863847 utils/get_test_modules.py: Support ansible.builtin. prefix
The ansible.builtin. prefix was not supported. Therefore tasks have not
been identified properly.
2023-01-09 18:04:07 +01:00
Thomas Woerner
e3bf82d873 utils/galaxyfy.py: Support builtins with ansible.builtin. prefix
The ansible builtins are using the ansible.builtin. prefix now, but
galaxyfy was not supporting the prefix. Therefore vars in set_fact tasks
got the collection prefix and include_role tasks have not been handled
correctly.
2023-01-09 17:36:56 +01:00
Thomas Woerner
76ca587d76 Merge pull request #1009 from rjeffman/ci_issue_995
upstream ci: Allow tasks to retry in case of connection failure.
2023-01-05 14:33:29 +01:00
Rafael Guterres Jeffman
5c630d6021 Merge pull request #1003 from dkarpele/dkarpele-2144724
Use netgroup_find instead of netgroup_show to workaround IPA bug.
2023-01-05 09:13:43 -03:00
Denis Karpelevich
483d51b418 Use netgroup_find instead of netgroup_show to workaround IPA bug.
Patch fixes https://bugzilla.redhat.com/show_bug.cgi?id=2144724 which
depends on https://pagure.io/freeipa/issue/9284.
Add comment why replacing `netgroup_show` with `netgroup_find`.

Signed-off-by: Denis Karpelevich <dkarpele@redhat.com>
2023-01-04 20:30:44 +01:00
Rafael Guterres Jeffman
ba353a9b16 Merge pull request #1007 from t-woerner/FQCN_ansible_builtin
Use FQCN for ansible.builtin
2023-01-03 16:24:19 -03:00
Rafael Guterres Jeffman
56560855b4 upstream ci: Allow tasks to retry in case of connection failure.
Some tasks used to setup Azure environment might fail to temporary
errors like timeouts and connection failures. Allowing the tasks to
retry a few times will allow the test to be correctly executed rather
than returning an error that is not related to the feature tested.
2022-12-26 12:15:06 -03:00
Rafael Guterres Jeffman
a8d44e2c52 Merge pull request #1002 from t-woerner/fix_spec_file_for_loop
ansible-freeipa.spec.in: Fix for loop with wildcard
2022-12-23 18:53:59 -03:00
Thomas Woerner
b175c78c95 vault: Use FQCN for ansible.builtin
Use Fully Qualified Collection Name (FQCN) for ansible builtins. This is
ansible.builtin.set_fact instead of set_fact for example and aplies for
all actions that are part of ansible.builtin.

All the replaced ansible.builtins:
  assert, command, copy, debug, fail, fetch, file, import_playbook,
  import_tasks, include_role, include_tasks, include_vars, package,
  set_fact, shell, slurp, stat, systemd
2022-12-20 13:55:19 +01:00
Thomas Woerner
198298b2d0 user: Use FQCN for ansible.builtin
Use Fully Qualified Collection Name (FQCN) for ansible builtins. This is
ansible.builtin.set_fact instead of set_fact for example and aplies for
all actions that are part of ansible.builtin.

All the replaced ansible.builtins:
  assert, command, copy, debug, fail, fetch, file, import_playbook,
  import_tasks, include_role, include_tasks, include_vars, package,
  set_fact, shell, slurp, stat, systemd
2022-12-20 13:55:03 +01:00
Thomas Woerner
d5269c83e6 trust: Use FQCN for ansible.builtin
Use Fully Qualified Collection Name (FQCN) for ansible builtins. This is
ansible.builtin.set_fact instead of set_fact for example and aplies for
all actions that are part of ansible.builtin.

All the replaced ansible.builtins:
  assert, command, copy, debug, fail, fetch, file, import_playbook,
  import_tasks, include_role, include_tasks, include_vars, package,
  set_fact, shell, slurp, stat, systemd
2022-12-20 13:54:42 +01:00
Thomas Woerner
9d47ffc2b9 sudo*: Use FQCN for ansible.builtin
Use Fully Qualified Collection Name (FQCN) for ansible builtins. This is
ansible.builtin.set_fact instead of set_fact for example and aplies for
all actions that are part of ansible.builtin.

All the replaced ansible.builtins:
  assert, command, copy, debug, fail, fetch, file, import_playbook,
  import_tasks, include_role, include_tasks, include_vars, package,
  set_fact, shell, slurp, stat, systemd
2022-12-20 13:54:26 +01:00
Thomas Woerner
feadbfce95 servicedelegation*: Use FQCN for ansible.builtin
Use Fully Qualified Collection Name (FQCN) for ansible builtins. This is
ansible.builtin.set_fact instead of set_fact for example and aplies for
all actions that are part of ansible.builtin.

All the replaced ansible.builtins:
  assert, command, copy, debug, fail, fetch, file, import_playbook,
  import_tasks, include_role, include_tasks, include_vars, package,
  set_fact, shell, slurp, stat, systemd
2022-12-20 13:54:01 +01:00
Thomas Woerner
a9257e7f44 service: Use FQCN for ansible.builtin
Use Fully Qualified Collection Name (FQCN) for ansible builtins. This is
ansible.builtin.set_fact instead of set_fact for example and aplies for
all actions that are part of ansible.builtin.

All the replaced ansible.builtins:
  assert, command, copy, debug, fail, fetch, file, import_playbook,
  import_tasks, include_role, include_tasks, include_vars, package,
  set_fact, shell, slurp, stat, systemd
2022-12-20 13:53:43 +01:00
Thomas Woerner
d204b6d480 server: Use FQCN for ansible.builtin
Use Fully Qualified Collection Name (FQCN) for ansible builtins. This is
ansible.builtin.set_fact instead of set_fact for example and aplies for
all actions that are part of ansible.builtin.

All the replaced ansible.builtins:
  assert, command, copy, debug, fail, fetch, file, import_playbook,
  import_tasks, include_role, include_tasks, include_vars, package,
  set_fact, shell, slurp, stat, systemd
2022-12-20 13:53:30 +01:00
Thomas Woerner
c645841444 selfservice: Use FQCN for ansible.builtin
Use Fully Qualified Collection Name (FQCN) for ansible builtins. This is
ansible.builtin.set_fact instead of set_fact for example and aplies for
all actions that are part of ansible.builtin.

All the replaced ansible.builtins:
  assert, command, copy, debug, fail, fetch, file, import_playbook,
  import_tasks, include_role, include_tasks, include_vars, package,
  set_fact, shell, slurp, stat, systemd
2022-12-20 13:52:52 +01:00
Thomas Woerner
f2a0edeb25 role: Use FQCN for ansible.builtin
Use Fully Qualified Collection Name (FQCN) for ansible builtins. This is
ansible.builtin.set_fact instead of set_fact for example and aplies for
all actions that are part of ansible.builtin.

All the replaced ansible.builtins:
  assert, command, copy, debug, fail, fetch, file, import_playbook,
  import_tasks, include_role, include_tasks, include_vars, package,
  set_fact, shell, slurp, stat, systemd
2022-12-20 13:52:36 +01:00
Thomas Woerner
45baf5c108 pwpolicy: Use FQCN for ansible.builtin
Use Fully Qualified Collection Name (FQCN) for ansible builtins. This is
ansible.builtin.set_fact instead of set_fact for example and aplies for
all actions that are part of ansible.builtin.

All the replaced ansible.builtins:
  assert, command, copy, debug, fail, fetch, file, import_playbook,
  import_tasks, include_role, include_tasks, include_vars, package,
  set_fact, shell, slurp, stat, systemd
2022-12-20 13:52:21 +01:00
Thomas Woerner
deec31c3ab privilege: Use FQCN for ansible.builtin
Use Fully Qualified Collection Name (FQCN) for ansible builtins. This is
ansible.builtin.set_fact instead of set_fact for example and aplies for
all actions that are part of ansible.builtin.

All the replaced ansible.builtins:
  assert, command, copy, debug, fail, fetch, file, import_playbook,
  import_tasks, include_role, include_tasks, include_vars, package,
  set_fact, shell, slurp, stat, systemd
2022-12-20 13:52:05 +01:00
Thomas Woerner
fea480b348 permission: Use FQCN for ansible.builtin
Use Fully Qualified Collection Name (FQCN) for ansible builtins. This is
ansible.builtin.set_fact instead of set_fact for example and aplies for
all actions that are part of ansible.builtin.

All the replaced ansible.builtins:
  assert, command, copy, debug, fail, fetch, file, import_playbook,
  import_tasks, include_role, include_tasks, include_vars, package,
  set_fact, shell, slurp, stat, systemd
2022-12-20 13:51:51 +01:00
Thomas Woerner
defd1e4e92 netgroup: Use FQCN for ansible.builtin
Use Fully Qualified Collection Name (FQCN) for ansible builtins. This is
ansible.builtin.set_fact instead of set_fact for example and aplies for
all actions that are part of ansible.builtin.

All the replaced ansible.builtins:
  assert, command, copy, debug, fail, fetch, file, import_playbook,
  import_tasks, include_role, include_tasks, include_vars, package,
  set_fact, shell, slurp, stat, systemd
2022-12-20 13:51:21 +01:00
Thomas Woerner
adc262bcb0 location: Use FQCN for ansible.builtin
Use Fully Qualified Collection Name (FQCN) for ansible builtins. This is
ansible.builtin.set_fact instead of set_fact for example and aplies for
all actions that are part of ansible.builtin.

All the replaced ansible.builtins:
  assert, command, copy, debug, fail, fetch, file, import_playbook,
  import_tasks, include_role, include_tasks, include_vars, package,
  set_fact, shell, slurp, stat, systemd
2022-12-20 13:50:58 +01:00
Thomas Woerner
72b4b89116 idrange: Use FQCN for ansible.builtin
Use Fully Qualified Collection Name (FQCN) for ansible builtins. This is
ansible.builtin.set_fact instead of set_fact for example and aplies for
all actions that are part of ansible.builtin.

All the replaced ansible.builtins:
  assert, command, copy, debug, fail, fetch, file, import_playbook,
  import_tasks, include_role, include_tasks, include_vars, package,
  set_fact, shell, slurp, stat, systemd
2022-12-20 13:50:46 +01:00
Thomas Woerner
473ed03e26 host*: Use FQCN for ansible.builtin
Use Fully Qualified Collection Name (FQCN) for ansible builtins. This is
ansible.builtin.set_fact instead of set_fact for example and aplies for
all actions that are part of ansible.builtin.

All the replaced ansible.builtins:
  assert, command, copy, debug, fail, fetch, file, import_playbook,
  import_tasks, include_role, include_tasks, include_vars, package,
  set_fact, shell, slurp, stat, systemd
2022-12-20 13:50:23 +01:00
Thomas Woerner
d546b4614d hbac*: Use FQCN for ansible.builtin
Use Fully Qualified Collection Name (FQCN) for ansible builtins. This is
ansible.builtin.set_fact instead of set_fact for example and aplies for
all actions that are part of ansible.builtin.

All the replaced ansible.builtins:
  assert, command, copy, debug, fail, fetch, file, import_playbook,
  import_tasks, include_role, include_tasks, include_vars, package,
  set_fact, shell, slurp, stat, systemd
2022-12-20 13:49:50 +01:00
Thomas Woerner
872537f4de group: Use FQCN for ansible.builtin
Use Fully Qualified Collection Name (FQCN) for ansible builtins. This is
ansible.builtin.set_fact instead of set_fact for example and aplies for
all actions that are part of ansible.builtin.

All the replaced ansible.builtins:
  assert, command, copy, debug, fail, fetch, file, import_playbook,
  import_tasks, include_role, include_tasks, include_vars, package,
  set_fact, shell, slurp, stat, systemd
2022-12-20 13:49:18 +01:00
Thomas Woerner
d6658347c9 tests/external-signed-ca-*: Use FQCN for ansible.builtin
Use Fully Qualified Collection Name (FQCN) for ansible builtins. This is
ansible.builtin.set_fact instead of set_fact for example and aplies for
all actions that are part of ansible.builtin.

All the replaced ansible.builtins:
  assert, command, copy, debug, fail, fetch, file, import_playbook,
  import_tasks, include_role, include_tasks, include_vars, package,
  set_fact, shell, slurp, stat, systemd
2022-12-20 13:48:37 +01:00
Thomas Woerner
062b53a676 tests/env_freeipa_facts.yml: Use FQCN for ansible.builtin
Use Fully Qualified Collection Name (FQCN) for ansible builtins. This is
ansible.builtin.set_fact instead of set_fact for example and aplies for
all actions that are part of ansible.builtin.

All the replaced ansible.builtins:
  assert, command, copy, debug, fail, fetch, file, import_playbook,
  import_tasks, include_role, include_tasks, include_vars, package,
  set_fact, shell, slurp, stat, systemd
2022-12-20 13:47:21 +01:00
Thomas Woerner
470d0ddc1b dnszone: Use FQCN for ansible.builtin
Use Fully Qualified Collection Name (FQCN) for ansible builtins. This is
ansible.builtin.set_fact instead of set_fact for example and aplies for
all actions that are part of ansible.builtin.

All the replaced ansible.builtins:
  assert, command, copy, debug, fail, fetch, file, import_playbook,
  import_tasks, include_role, include_tasks, include_vars, package,
  set_fact, shell, slurp, stat, systemd
2022-12-20 13:46:54 +01:00
Thomas Woerner
2e707a48cb dnsrecord: Use FQCN for ansible.builtin
Use Fully Qualified Collection Name (FQCN) for ansible builtins. This is
ansible.builtin.set_fact instead of set_fact for example and aplies for
all actions that are part of ansible.builtin.

All the replaced ansible.builtins:
  assert, command, copy, debug, fail, fetch, file, import_playbook,
  import_tasks, include_role, include_tasks, include_vars, package,
  set_fact, shell, slurp, stat, systemd
2022-12-20 13:46:36 +01:00
Thomas Woerner
971d40c3a9 dnsforwardzone: Use FQCN for ansible.builtin
Use Fully Qualified Collection Name (FQCN) for ansible builtins. This is
ansible.builtin.set_fact instead of set_fact for example and aplies for
all actions that are part of ansible.builtin.

All the replaced ansible.builtins:
  assert, command, copy, debug, fail, fetch, file, import_playbook,
  import_tasks, include_role, include_tasks, include_vars, package,
  set_fact, shell, slurp, stat, systemd
2022-12-20 13:46:10 +01:00
Thomas Woerner
7d89af48b6 dnsconfig: Use FQCN for ansible.builtin
Use Fully Qualified Collection Name (FQCN) for ansible builtins. This is
ansible.builtin.set_fact instead of set_fact for example and aplies for
all actions that are part of ansible.builtin.

All the replaced ansible.builtins:
  assert, command, copy, debug, fail, fetch, file, import_playbook,
  import_tasks, include_role, include_tasks, include_vars, package,
  set_fact, shell, slurp, stat, systemd
2022-12-20 13:45:44 +01:00
Thomas Woerner
03ce096fbb delegation: Use FQCN for ansible.builtin
Use Fully Qualified Collection Name (FQCN) for ansible builtins. This is
ansible.builtin.set_fact instead of set_fact for example and aplies for
all actions that are part of ansible.builtin.

All the replaced ansible.builtins:
  assert, command, copy, debug, fail, fetch, file, import_playbook,
  import_tasks, include_role, include_tasks, include_vars, package,
  set_fact, shell, slurp, stat, systemd
2022-12-20 13:45:08 +01:00
Thomas Woerner
91edff3b21 config: Use FQCN for ansible.builtin
Use Fully Qualified Collection Name (FQCN) for ansible builtins. This is
ansible.builtin.set_fact instead of set_fact for example and aplies for
all actions that are part of ansible.builtin.

All the replaced ansible.builtins:
  assert, command, copy, debug, fail, fetch, file, import_playbook,
  import_tasks, include_role, include_tasks, include_vars, package,
  set_fact, shell, slurp, stat, systemd
2022-12-20 13:44:38 +01:00
Thomas Woerner
84c0188023 tests/ca-less: Use FQCN for ansible.builtin
Use Fully Qualified Collection Name (FQCN) for ansible builtins. This is
ansible.builtin.set_fact instead of set_fact for example and aplies for
all actions that are part of ansible.builtin.

All the replaced ansible.builtins:
  assert, command, copy, debug, fail, fetch, file, import_playbook,
  import_tasks, include_role, include_tasks, include_vars, package,
  set_fact, shell, slurp, stat, systemd
2022-12-20 13:43:59 +01:00
Thomas Woerner
1f91730b17 automount: Use FQCN for ansible.builtin
Use Fully Qualified Collection Name (FQCN) for ansible builtins. This is
ansible.builtin.set_fact instead of set_fact for example and aplies for
all actions that are part of ansible.builtin.

All the replaced ansible.builtins:
  assert, command, copy, debug, fail, fetch, file, import_playbook,
  import_tasks, include_role, include_tasks, include_vars, package,
  set_fact, shell, slurp, stat, systemd
2022-12-20 13:41:14 +01:00
Thomas Woerner
99c7acbe5f automember: Use FQCN for ansible.builtin
Use Fully Qualified Collection Name (FQCN) for ansible builtins. This is
ansible.builtin.set_fact instead of set_fact for example and aplies for
all actions that are part of ansible.builtin.

All the replaced ansible.builtins:
  assert, command, copy, debug, fail, fetch, file, import_playbook,
  import_tasks, include_role, include_tasks, include_vars, package,
  set_fact, shell, slurp, stat, systemd
2022-12-20 13:40:09 +01:00
Thomas Woerner
14706cc49e ipabackup role: Use FQCN for ansible.builtin
Use Fully Qualified Collection Name (FQCN) for ansible builtins. This is
ansible.builtin.set_fact instead of set_fact for example and aplies for
all actions that are part of ansible.builtin.

All the replaced ansible.builtins:
  assert, command, copy, debug, fail, fetch, file, import_playbook,
  import_tasks, include_role, include_tasks, include_vars, package,
  set_fact, shell, slurp, stat, systemd
2022-12-20 13:38:30 +01:00
Thomas Woerner
dde5b06b97 ipaclient role: Use FQCN for ansible.builtin
Use Fully Qualified Collection Name (FQCN) for ansible builtins. This is
ansible.builtin.set_fact instead of set_fact for example and aplies for
all actions that are part of ansible.builtin.

All the replaced ansible.builtins:
  assert, command, copy, debug, fail, fetch, file, import_playbook,
  import_tasks, include_role, include_tasks, include_vars, package,
  set_fact, shell, slurp, stat, systemd
2022-12-20 13:37:07 +01:00
Thomas Woerner
c7e83685e3 ipareplica role: Use FQCN for ansible.builtin
Use Fully Qualified Collection Name (FQCN) for ansible builtins. This is
ansible.builtin.set_fact instead of set_fact for example and aplies for
all actions that are part of ansible.builtin.

All the replaced ansible.builtins:
  assert, command, copy, debug, fail, fetch, file, import_playbook,
  import_tasks, include_role, include_tasks, include_vars, package,
  set_fact, shell, slurp, stat, systemd
2022-12-20 13:36:32 +01:00
Thomas Woerner
882d60515d ipaserver role: Use FQCN for ansible.builtin
Use Fully Qualified Collection Name (FQCN) for ansible builtins. This is
ansible.builtin.set_fact instead of set_fact for example and aplies for
all actions that are part of ansible.builtin.

All the replaced ansible.builtins:
  assert, command, copy, debug, fail, fetch, file, import_playbook,
  import_tasks, include_role, include_tasks, include_vars, package,
  set_fact, shell, slurp, stat, systemd
2022-12-20 13:35:03 +01:00
Thomas Woerner
27cbd40182 ansible-freeipa.spec.in: Fix for loop with wildcard
The issue within the for loops to remove python shebangs and to remove the
execution flag from python files has been solved.
2022-12-06 10:18:08 +01:00
252 changed files with 4042 additions and 1571 deletions

View File

@@ -16,6 +16,11 @@ exclude_paths:
kinds:
- playbook: '**/tests/**/test_*.yml'
- playbook: '**/playbooks/**/*.yml'
- playbook: '**/tests/ca-less/install_*_without_ca.yml'
- playbook: '**/tests/ca-less/clean_up_certificates.yml'
- playbook: '**/tests/external-signed-ca-with-automatic-copy/install-server-with-external-ca-with-automatic-copy.yml'
- playbook: '**/tests/external-signed-ca-with-manual-copy/install-server-with-external-ca-with-manual-copy.yml'
- playbook: '**/tests/user/create_users_json.yml'
- tasks: '**/tasks_*.yml'
- tasks: '**/env_*.yml'
@@ -28,6 +33,8 @@ skip_list:
- '305' # Use shell only when shell functionality is required
- '306' # risky-shell-pipe
- yaml # yamllint should be executed separately.
- experimental # Do not run any experimental tests
- name[template] # Allow Jinja templating inside task names
use_default_rules: true

View File

@@ -4,41 +4,7 @@ on:
- push
- pull_request
jobs:
check_docs_29:
name: Check Ansible Documentation with Ansible 2.9.
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v3.1.0
with:
fetch-depth: 0
- uses: actions/setup-python@v4.3.0
with:
python-version: '3.x'
- name: Install Ansible 2.9
run: |
python -m pip install "ansible < 2.10"
- name: Run ansible-doc-test
run: |
ANSIBLE_LIBRARY="." ANSIBLE_DOC_FRAGMENT_PLUGINS="." python utils/ansible-doc-test -v roles plugins
check_docs_2_11:
name: Check Ansible Documentation with ansible-core 2.11.
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v3.1.0
with:
fetch-depth: 0
- uses: actions/setup-python@v4.3.0
with:
python-version: '3.x'
- name: Install Ansible 2.11
run: |
python -m pip install "ansible-core >=2.11,<2.12"
- name: Run ansible-doc-test
run: |
ANSIBLE_LIBRARY="." ANSIBLE_DOC_FRAGMENT_PLUGINS="." python utils/ansible-doc-test -v roles plugins
check_docs_2_12:
check_docs_oldest_supported:
name: Check Ansible Documentation with ansible-core 2.12.
runs-on: ubuntu-latest
steps:
@@ -53,10 +19,43 @@ jobs:
python -m pip install "ansible-core >=2.12,<2.13"
- name: Run ansible-doc-test
run: |
python -m pip install "ansible-core >=2.12,<2.13"
ANSIBLE_LIBRARY="." ANSIBLE_DOC_FRAGMENT_PLUGINS="." python utils/ansible-doc-test -v roles plugins
check_docs_latest:
check_docs_previous:
name: Check Ansible Documentation with ansible-core 2.13.
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v3.1.0
with:
fetch-depth: 0
- uses: actions/setup-python@v4.3.0
with:
python-version: '3.x'
- name: Install Ansible 2.13
run: |
python -m pip install "ansible-core >=2.13,<2.14"
- name: Run ansible-doc-test
run: |
ANSIBLE_LIBRARY="." ANSIBLE_DOC_FRAGMENT_PLUGINS="." python utils/ansible-doc-test -v roles plugins
check_docs_current:
name: Check Ansible Documentation with ansible-core 2.14.
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v3.1.0
with:
fetch-depth: 0
- uses: actions/setup-python@v4.3.0
with:
python-version: '3.x'
- name: Install Ansible 2.14
run: |
python -m pip install "ansible-core >=2.14,<2.15"
- name: Run ansible-doc-test
run: |
ANSIBLE_LIBRARY="." ANSIBLE_DOC_FRAGMENT_PLUGINS="." python utils/ansible-doc-test -v roles plugins
check_docs_ansible_latest:
name: Check Ansible Documentation with latest Ansible version.
runs-on: ubuntu-latest
steps:

View File

@@ -16,12 +16,10 @@ jobs:
python-version: "3.x"
- name: Run ansible-lint
run: |
pip install ansible-core==2.11.6 ansible-lint
find playbooks roles tests -name '*.yml' ! -name "env_*" ! -name "tasks_*" -exec ansible-lint --force-color {} \+
env:
ANSIBLE_MODULE_UTILS: plugins/module_utils
ANSIBLE_LIBRARY: plugins/modules
ANSIBLE_DOC_FRAGMENT_PLUGINS: plugins/doc_fragments
pip install "ansible-core >=2.14,<2.15" ansible-lint
utils/build-galaxy-release.sh -ki
cd .galaxy-build
ansible-lint
yamllint:
name: Verify yamllint
@@ -34,7 +32,7 @@ jobs:
with:
python-version: "3.x"
- name: Run yaml-lint
uses: ibiqlik/action-yamllint@v1
uses: ibiqlik/action-yamllint@v3.1.1
pydocstyle:
name: Verify pydocstyle
@@ -63,7 +61,7 @@ jobs:
python-version: "3.x"
- name: Run flake8
run: |
pip install flake8
pip install flake8 flake8-bugbear
flake8
pylint:
@@ -78,7 +76,7 @@ jobs:
python-version: "3.x"
- name: Run pylint
run: |
pip install pylint==2.13.7 wrapt==1.14.0
pip install pylint==2.14.4 wrapt==1.14.0
pylint plugins roles --disable=import-error
shellcheck:

View File

@@ -1,7 +1,7 @@
---
repos:
- repo: https://github.com/ansible/ansible-lint.git
rev: v5.3.2
rev: v6.6.1
hooks:
- id: ansible-lint
always_run: false
@@ -11,20 +11,20 @@ repos:
entry: |
env ANSIBLE_LIBRARY=./plugins/modules ANSIBLE_MODULE_UTILS=./plugins/module_utils ANSIBLE_DOC_FRAGMENT_PLUGINS=./plugins/doc_fragments ansible-lint
- repo: https://github.com/adrienverge/yamllint.git
rev: v1.26.1
rev: v1.28.0
hooks:
- id: yamllint
files: \.(yaml|yml)$
- repo: https://github.com/pycqa/flake8
rev: 3.9.2
rev: 5.0.3
hooks:
- id: flake8
- repo: https://github.com/pycqa/pydocstyle
rev: 6.1.1
rev: 6.0.0
hooks:
- id: pydocstyle
- repo: https://github.com/pycqa/pylint
rev: v2.12.2
rev: v2.14.4
hooks:
- id: pylint
args:

View File

@@ -8,6 +8,9 @@ The group module allows to ensure presence and absence of groups and members of
The group module is as compatible as possible to the Ansible upstream `ipa_group` module, but additionally offers to add users to a group and also to remove users from a group.
## Note
Ensuring presence (adding) of several groups with mixed types (`external`, `nonposix` and `posix`) requires a fix in FreeIPA. The module implements a workaround to automatically use `client` context if the fix is not present in the target node FreeIPA and if more than one group is provided to the task using the `groups` parameter. If `ipaapi_context` is forced to be `server`, the module will fail in this case.
Features
--------
@@ -71,6 +74,62 @@ Example playbook to add groups:
name: appops
```
These three `ipagroup` module calls can be combined into one with the `groups` variable:
```yaml
---
- name: Playbook to handle groups
hosts: ipaserver
tasks:
- name: Ensure groups ops, sysops and appops are present
ipagroup:
ipaadmin_password: SomeADMINpassword
groups:
- name: ops
gidnumber: 1234
- name: sysops
user:
- pinky
- name: appops
```
You can also alternatively use a json file containing the groups, here `groups_present.json`:
```json
{
"groups": [
{
"name": "group1",
"description": "description group1"
},
{
"name": "group2",
"description": "description group2"
}
]
}
```
And ensure the presence of the groups with this example playbook:
```yaml
---
- name: Tests
hosts: ipaserver
gather_facts: false
tasks:
- name: Include groups_present.json
include_vars:
file: groups_present.json
- name: Groups present
ipagroup:
ipaadmin_password: SomeADMINpassword
groups: "{{ groups }}"
```
Example playbook to add users to a group:
```yaml
@@ -112,11 +171,11 @@ Example playbook to add group members to a group:
Example playbook to add members from a trusted realm to an external group:
```yaml
--
---
- name: Playbook to handle groups.
hosts: ipaserver
became: true
tasks:
- name: Create an external group and add members from a trust to it.
ipagroup:
ipaadmin_password: SomeADMINpassword
@@ -127,6 +186,24 @@ Example playbook to add members from a trusted realm to an external group:
- WINIPA\\Developers
```
Example playbook to add nonposix and external groups:
```yaml
---
- name: Playbook to add nonposix and external groups
hosts: ipaserver
tasks:
- name: Add nonposix group sysops and external group appops
ipagroup:
ipaadmin_password: SomeADMINpassword
groups:
- name: sysops
nonposix: true
- name: appops
external: true
```
Example playbook to remove groups:
```yaml
@@ -136,13 +213,29 @@ Example playbook to remove groups:
become: true
tasks:
# Remove goups sysops, appops and ops
# Remove groups sysops, appops and ops
- ipagroup:
ipaadmin_password: SomeADMINpassword
name: sysops,appops,ops
state: absent
```
Example playbook to ensure groups are absent:
```yaml
---
- name: Playbook to handle groups
hosts: ipaserver
tasks:
- name: Ensure groups ops and sysops are absent
ipagroup:
ipaadmin_password: SomeADMINpassword
groups:
- name: ops
- name: sysops
state: absent
```
Variables
=========
@@ -152,8 +245,10 @@ Variable | Description | Required
`ipaadmin_principal` | The admin principal is a string and defaults to `admin` | no
`ipaadmin_password` | The admin password is a string and is required if there is no admin ticket available on the node | no
`ipaapi_context` | The context in which the module will execute. Executing in a server context is preferred. If not provided context will be determined by the execution environment. Valid values are `server` and `client`. | no
`ipaapi_ldap_cache` | Use LDAP cache for IPA connection. The bool setting defaults to yes. (bool) | no
`ipaapi_ldap_cache` | Use LDAP cache for IPA connection. The bool setting defaults to <br/>. (bool) | no
`name` \| `cn` | The list of group name strings. | no
`groups` | The list of group dicts. Each `groups` dict entry can contain group variables.<br>There is one required option in the `groups` dict:| no
&nbsp; | `name` - The group name string of the entry. | yes
`description` | The group description string. | no
`gid` \| `gidnumber` | The GID integer. | no
`posix` | Create a non-POSIX group or change a non-POSIX to a posix group. `nonposix`, `posix` and `external` are mutually exclusive. (bool) | no

View File

@@ -372,8 +372,8 @@ There are only return values if one or more random passwords have been generated
Variable | Description | Returned When
-------- | ----------- | -------------
`host` | Host dict with random password. (dict) <br>Options: | If random is yes and host did not exist or update_password is yes
&nbsp; | `randompassword` - The generated random password | If only one host is handled by the module
&nbsp; | `name` - The host name of the host that got a new random password. (dict) <br> Options: <br> &nbsp; `randompassword` - The generated random password | If several hosts are handled by the module
&nbsp; | `randompassword` - The generated random password | If only one host is handled by the module without using the `hosts` parameter.
&nbsp; | `name` - The host name of the host that got a new random password. (dict) <br> Options: <br> &nbsp; `randompassword` - The generated random password | If several hosts are handled by the module with the `hosts` parameter.
Authors

View File

@@ -393,8 +393,8 @@ Variable | Description | Required
`passwordexpiration` \| `krbpasswordexpiration` | The kerberos password expiration date. Possible formats: `YYYYMMddHHmmssZ`, `YYYY-MM-ddTHH:mm:ssZ`, `YYYY-MM-ddTHH:mmZ`, `YYYY-MM-ddZ`, `YYYY-MM-dd HH:mm:ssZ` or `YYYY-MM-dd HH:mmZ`. The trailing 'Z' can be skipped. Only usable with IPA versions 4.7 and up. | no
`password` | The user password string. | no
`random` | Generate a random user password | no
`uid` \| `uidnumber` | The UID integer. | no
`gid` \| `gidnumber` | The GID integer. | no
`uid` \| `uidnumber` | User ID Number (system will assign one if not provided). | no
`gid` \| `gidnumber` | Group ID Number. | no
`city` | City | no
`userstate` \| `st` | State/Province | no
`postalcode` \| `zip` | Postalcode/ZIP | no
@@ -434,8 +434,8 @@ There are only return values if one or more random passwords have been generated
Variable | Description | Returned When
-------- | ----------- | -------------
`user` | User dict with random password. (dict) <br>Options: | If random is yes and user did not exist or update_password is yes
&nbsp; | `randompassword` - The generated random password | If only one user is handled by the module
&nbsp; | `name` - The user name of the user that got a new random password. (dict) <br> Options: <br> &nbsp; `randompassword` - The generated random password | If several users are handled by the module
&nbsp; | `randompassword` - The generated random password | If only one user is handled by the module without using the `users` parameter.
&nbsp; | `name` - The user name of the user that got a new random password. (dict) <br> Options: <br> &nbsp; `randompassword` - The generated random password | If several users are handled by the module with the `users` parameter.
Authors

View File

@@ -13,8 +13,8 @@ homepage: "https://github.com/freeipa/ansible-freeipa"
issues: "https://github.com/freeipa/ansible-freeipa/issues"
readme: "README.md"
license: "GPL-3.0-or-later"
license:
- "GPL-3.0-or-later"
tags:
- "linux"
- "system"

View File

@@ -4,7 +4,7 @@
become: no
tasks:
- name: ensure map TestMap is absent
- name: Ensure map TestMap is absent
ipaautomountmap:
ipaadmin_password: SomeADMINpassword
name: TestMap

View File

@@ -4,7 +4,7 @@
become: no
tasks:
- name: ensure map TestMap is present
- name: Ensure map TestMap is present
ipaautomountmap:
ipaadmin_password: SomeADMINpassword
name: TestMap

View File

@@ -11,5 +11,5 @@
register: serverconfig
- name: Display current configuration.
debug:
ansible.builtin.debug:
msg: "{{ serverconfig }}"

View File

@@ -5,7 +5,7 @@
gather_facts: no
tasks:
- name: set ca_renewal_master_server
- name: Set ca_renewal_master_server
ipaconfig:
ipaadmin_password: SomeADMINpassword
ca_renewal_master_server: carenewal.example.com

View File

@@ -1,5 +1,5 @@
---
- name: dnszone present
- name: All dnszone parameters
hosts: ipaserver
become: true

View File

@@ -1,5 +1,5 @@
---
- name: dnszone present
- name: Dnszone present
hosts: ipaserver
become: true

View File

@@ -11,5 +11,5 @@
register: result
- name: Zone name inferred from `name_from_ip`
debug:
ansible.builtin.debug:
msg: "Zone created: {{ result.dnszone.name }}"

View File

@@ -0,0 +1,32 @@
---
- name: Playbook to handle multiple groups
hosts: ipaserver
tasks:
- name: Create multiple groups ops, sysops
ipagroup:
ipaadmin_password: SomeADMINpassword
groups:
- name: ops
gidnumber: 1234
- name: sysops
- name: Add user and group members to groups sysops and appops
ipagroup:
ipaadmin_password: SomeADMINpassword
groups:
- name: sysops
user:
- user1
- name: appops
group:
- group2
- name: Create multiple non-POSIX and external groups
ipagroup:
ipaadmin_password: SomeADMINpassword
groups:
- name: nongroup
nonposix: true
- name: extgroup
external: true

View File

@@ -14,5 +14,5 @@
register: ipahost
- name: Print generated random password
debug:
ansible.builtin.debug:
var: ipahost.host.randompassword

View File

@@ -13,5 +13,5 @@
register: ipahost
- name: Print generated random password
debug:
ansible.builtin.debug:
var: ipahost.host.randompassword

View File

@@ -17,9 +17,9 @@
register: ipahost
- name: Print generated random password for host01.example.com
debug:
ansible.builtin.debug:
var: ipahost.host["host01.example.com"].randompassword
- name: Print generated random password for host02.example.com
debug:
ansible.builtin.debug:
var: ipahost.host["host02.example.com"].randompassword

View File

@@ -4,7 +4,7 @@
become: true
tasks:
- name: ensure the trust is present
- name: Ensure the trust is present
ipatrust:
ipaadmin_password: SomeADMINpassword
realm: windows.local

View File

@@ -4,7 +4,7 @@
become: true
tasks:
- name: ensure the trust is absent
- name: Ensure the trust is absent
ipatrust:
ipaadmin_password: SomeADMINpassword
realm: windows.local

View File

@@ -15,5 +15,5 @@
register: ipauser
- name: Print generated random password
debug:
ansible.builtin.debug:
var: ipauser.user.randompassword

View File

@@ -20,9 +20,9 @@
register: ipauser
- name: Print generated random password for user1
debug:
ansible.builtin.debug:
var: ipauser.user.user1.randompassword
- name: Print generated random password for user2
debug:
ansible.builtin.debug:
var: ipauser.user.user2.randompassword

View File

@@ -15,5 +15,5 @@
register: result
no_log: true
- name: Display retrieved data.
debug:
ansible.builtin.debug:
msg: "Data: {{ result.vault.data }}"

View File

@@ -15,5 +15,5 @@
register: result
no_log: true
- name: Display retrieved data.
debug:
ansible.builtin.debug:
msg: "Data: {{ result.vault.data }}"

View File

@@ -6,7 +6,7 @@
tasks:
- name: Copy file containing password to server.
copy:
ansible.builtin.copy:
src: "{{ playbook_dir }}/password.txt"
dest: "{{ ansible_facts['env'].HOME }}/password.txt"
owner: "{{ ansible_user }}"
@@ -20,6 +20,6 @@
vault_type: symmetric
vault_password_file: "{{ ansible_facts['env'].HOME }}/password.txt"
- name: Remove file containing password from server.
file:
ansible.builtin.file:
path: "{{ ansible_facts['env'].HOME }}/password.txt"
state: absent

View File

@@ -11,7 +11,7 @@
tasks:
- name: Copy public key file to server.
copy:
ansible.builtin.copy:
src: "{{ playbook_dir }}/public.pem"
dest: "{{ ansible_facts['env'].HOME }}/public.pem"
owner: "{{ ansible_user }}"
@@ -25,6 +25,6 @@
vault_type: asymmetric
vault_public_key_file: "{{ ansible_facts['env'].HOME }}/public.pem"
- name: Remove public key file from server.
file:
ansible.builtin.file:
path: "{{ ansible_facts['env'].HOME }}/public.pem"
state: absent

View File

@@ -1125,8 +1125,8 @@ class IPAAnsibleModule(AnsibleModule):
def ipa_get_domain(self):
"""Retrieve IPA API domain."""
if not hasattr(self, "__ipa_api_domain"):
setattr(self, "__ipa_api_domain", api_get_domain())
return getattr(self, "__ipa_api_domain")
setattr(self, "__ipa_api_domain", api_get_domain()) # noqa: B010
return getattr(self, "__ipa_api_domain") # noqa: B009
@staticmethod
def ipa_get_realm():

View File

@@ -126,7 +126,7 @@ class AutomountMap(IPAAnsibleModule):
self.params_fail_used_invalid(invalid, state)
def get_args(self, mapname, desc): # pylint: disable=no-self-use
def get_args(self, mapname, desc):
# automountmapname is required for all automountmap operations.
if not mapname:
self.fail_json(msg="automountmapname cannot be None or empty.")

View File

@@ -280,7 +280,8 @@ class DNSZoneModule(IPAAnsibleModule):
if any(invalid_ips):
self.fail_json(msg=error_msg % invalid_ips)
def is_valid_nsec3param_rec(self, nsec3param_rec): # pylint: disable=R0201
@staticmethod
def is_valid_nsec3param_rec(nsec3param_rec):
try:
part1, part2, part3, part4 = nsec3param_rec.split(" ")
except ValueError:

View File

@@ -41,8 +41,88 @@ options:
description: The group name
type: list
elements: str
required: true
required: false
aliases: ["cn"]
groups:
description: The list of group dicts (internally gid).
type: list
elements: dict
suboptions:
name:
description: The group (internally gid).
type: str
required: true
aliases: ["cn"]
description:
description: The group description
type: str
required: false
gid:
description: The GID
type: int
required: false
aliases: ["gidnumber"]
nonposix:
description: Create as a non-POSIX group
required: false
type: bool
external:
description: Allow adding external non-IPA members from trusted domains
required: false
type: bool
posix:
description:
Create a non-POSIX group or change a non-POSIX to a posix group.
required: false
type: bool
nomembers:
description: Suppress processing of membership attributes
required: false
type: bool
user:
description: List of user names assigned to this group.
required: false
type: list
elements: str
group:
description: List of group names assigned to this group.
required: false
type: list
elements: str
service:
description:
- List of service names assigned to this group.
- Only usable with IPA versions 4.7 and up.
required: false
type: list
elements: str
membermanager_user:
description:
- List of member manager users assigned to this group.
- Only usable with IPA versions 4.8.4 and up.
required: false
type: list
elements: str
membermanager_group:
description:
- List of member manager groups assigned to this group.
- Only usable with IPA versions 4.8.4 and up.
required: false
type: list
elements: str
externalmember:
description:
- List of members of a trusted domain in DOM\\name or name@domain form.
required: false
type: list
elements: str
aliases: ["ipaexternalmember", "external_member"]
idoverrideuser:
description:
- User ID overrides to add
required: false
type: list
elements: str
description:
description: The group description
type: str
@@ -144,6 +224,14 @@ EXAMPLES = """
ipaadmin_password: SomeADMINpassword
name: appops
# Create multiple groups ops, sysops
- ipagroup:
ipaadmin_password: SomeADMINpassword
groups:
- name: ops
gidnumber: 1234
- name: sysops
# Add user member pinky to group sysops
- ipagroup:
ipaadmin_password: SomeADMINpassword
@@ -160,7 +248,7 @@ EXAMPLES = """
user:
- brain
# Add group members sysops and appops to group sysops
# Add group members sysops and appops to group ops
- ipagroup:
ipaadmin_password: SomeADMINpassword
name: ops
@@ -168,6 +256,17 @@ EXAMPLES = """
- sysops
- appops
# Add user and group members to groups sysops and appops
- ipagroup:
ipaadmin_password: SomeADMINpassword
groups:
- name: sysops
user:
- user1
- name: appops
group:
- group2
# Create a non-POSIX group
- ipagroup:
ipaadmin_password: SomeADMINpassword
@@ -189,7 +288,16 @@ EXAMPLES = """
- WINIPA\\Web Users
- WINIPA\\Developers
# Remove goups sysops, appops, ops and nongroup
# Create multiple non-POSIX and external groups
- ipagroup:
ipaadmin_password: SomeADMINpassword
groups:
- name: nongroup
nonposix: true
- name: extgroup
external: true
# Remove groups sysops, appops, ops and nongroup
- ipagroup:
ipaadmin_password: SomeADMINpassword
name: sysops,appops,ops, nongroup
@@ -203,6 +311,20 @@ from ansible.module_utils._text import to_text
from ansible.module_utils.ansible_freeipa_module import \
IPAAnsibleModule, compare_args_ipa, gen_add_del_lists, \
gen_add_list, gen_intersection_list, api_check_param
from ansible.module_utils import six
if six.PY3:
unicode = str
# Ensuring (adding) several groups with mixed types external, nonposix
# and posix require to have a fix in IPA:
# FreeIPA issue: https://pagure.io/freeipa/issue/9349
# FreeIPA fix: https://github.com/freeipa/freeipa/pull/6741
try:
from ipaserver.plugins import baseldap
except ImportError:
FIX_6741_DEEPCOPY_OBJECTCLASSES = False
else:
FIX_6741_DEEPCOPY_OBJECTCLASSES = \
"deepcopy" in baseldap.LDAPObject.__json__.__code__.co_names
def find_group(module, name):
@@ -257,6 +379,22 @@ def gen_member_args(user, group, service, externalmember, idoverrideuser):
return _args
def check_parameters(module, state, action):
invalid = []
if state == "present":
if action == "member":
invalid = ["description", "gid", "posix", "nonposix", "external",
"nomembers"]
else:
invalid = ["description", "gid", "posix", "nonposix", "external",
"nomembers"]
if action == "group":
invalid.extend(["user", "group", "service", "externalmember"])
module.params_fail_used_invalid(invalid, state, action)
def is_external_group(res_find):
"""Verify if the result group is an external group."""
return res_find and 'ipaexternalgroup' in res_find['objectclass']
@@ -285,45 +423,63 @@ def check_objectclass_args(module, res_find, posix, external):
def main():
group_spec = dict(
# present
description=dict(type="str", default=None),
gid=dict(type="int", aliases=["gidnumber"], default=None),
nonposix=dict(required=False, type='bool', default=None),
external=dict(required=False, type='bool', default=None),
posix=dict(required=False, type='bool', default=None),
nomembers=dict(required=False, type='bool', default=None),
user=dict(required=False, type='list', elements="str",
default=None),
group=dict(required=False, type='list', elements="str",
default=None),
service=dict(required=False, type='list', elements="str",
default=None),
idoverrideuser=dict(required=False, type='list', elements="str",
default=None),
membermanager_user=dict(required=False, type='list',
elements="str", default=None),
membermanager_group=dict(required=False, type='list',
elements="str", default=None),
externalmember=dict(required=False, type='list', elements="str",
default=None,
aliases=[
"ipaexternalmember",
"external_member"
])
)
ansible_module = IPAAnsibleModule(
argument_spec=dict(
# general
name=dict(type="list", elements="str", aliases=["cn"],
required=True),
# present
description=dict(type="str", default=None),
gid=dict(type="int", aliases=["gidnumber"], default=None),
nonposix=dict(required=False, type='bool', default=None),
external=dict(required=False, type='bool', default=None),
posix=dict(required=False, type='bool', default=None),
nomembers=dict(required=False, type='bool', default=None),
user=dict(required=False, type='list', elements="str",
default=None),
group=dict(required=False, type='list', elements="str",
default=None),
service=dict(required=False, type='list', elements="str",
default=None),
idoverrideuser=dict(required=False, type='list', elements="str",
default=None),
membermanager_user=dict(required=False, type='list',
elements="str", default=None),
membermanager_group=dict(required=False, type='list',
elements="str", default=None),
externalmember=dict(required=False, type='list', elements="str",
default=None,
aliases=[
"ipaexternalmember",
"external_member"
]),
default=None, required=False),
groups=dict(type="list",
default=None,
options=dict(
# Here name is a simple string
name=dict(type="str", required=True,
aliases=["cn"]),
# Add group specific parameters
**group_spec
),
elements='dict',
required=False),
# general
action=dict(type="str", default="group",
choices=["member", "group"]),
# state
state=dict(type="str", default="present",
choices=["present", "absent"]),
# Add group specific parameters for simple use case
**group_spec
),
# It does not make sense to set posix, nonposix or external at the
# same time
mutually_exclusive=[['posix', 'nonposix', 'external']],
mutually_exclusive=[['posix', 'nonposix', 'external'],
["name", "groups"]],
required_one_of=[["name", "groups"]],
supports_check_mode=True,
)
@@ -333,6 +489,7 @@ def main():
# general
names = ansible_module.params_get("name")
groups = ansible_module.params_get("groups")
# present
description = ansible_module.params_get("description")
@@ -354,31 +511,50 @@ def main():
state = ansible_module.params_get("state")
# Check parameters
invalid = []
if (names is None or len(names) < 1) and \
(groups is None or len(groups) < 1):
ansible_module.fail_json(msg="At least one name or groups is required")
if state == "present":
if len(names) != 1:
if names is not None and len(names) != 1:
ansible_module.fail_json(
msg="Only one group can be added at a time.")
if action == "member":
invalid = ["description", "gid", "posix", "nonposix", "external",
"nomembers"]
msg="Only one group can be added at a time using 'name'.")
if state == "absent":
if len(names) < 1:
ansible_module.fail_json(
msg="No name given.")
invalid = ["description", "gid", "posix", "nonposix", "external",
"nomembers"]
if action == "group":
invalid.extend(["user", "group", "service", "externalmember"])
ansible_module.params_fail_used_invalid(invalid, state, action)
check_parameters(ansible_module, state, action)
if external is False:
ansible_module.fail_json(
msg="group can not be non-external")
# Ensuring (adding) several groups with mixed types external, nonposix
# and posix require to have a fix in IPA:
#
# FreeIPA issue: https://pagure.io/freeipa/issue/9349
# FreeIPA fix: https://github.com/freeipa/freeipa/pull/6741
#
# The simple solution is to switch to client context for ensuring
# several groups simply if the user was not explicitly asking for
# the server context no matter if mixed types are used.
context = None
if state == "present" and groups is not None and len(groups) > 1 \
and not FIX_6741_DEEPCOPY_OBJECTCLASSES:
_context = ansible_module.params_get("ipaapi_context")
if _context is None:
context = "client"
ansible_module.debug(
"Switching to client context due to an unfixed issue in "
"your IPA version: https://pagure.io/freeipa/issue/9349")
elif _context == "server":
ansible_module.fail_json(
msg="Ensuring several groups with server context is not "
"supported by your IPA version: "
"https://pagure.io/freeipa/issue/9349")
# Use groups if names is None
if groups is not None:
names = groups
# Init
changed = False
@@ -389,7 +565,7 @@ def main():
posix = not nonposix
# Connect to IPA API
with ansible_module.ipa_connect():
with ansible_module.ipa_connect(context=context):
has_add_member_service = ansible_module.ipa_command_param_exists(
"group_add_member", "service")
@@ -415,8 +591,57 @@ def main():
"supported by your IPA version")
commands = []
group_set = set()
for group_name in names:
if isinstance(group_name, dict):
name = group_name.get("name")
if name in group_set:
ansible_module.fail_json(
msg="group '%s' is used more than once" % name)
group_set.add(name)
# present
description = group_name.get("description")
gid = group_name.get("gid")
nonposix = group_name.get("nonposix")
external = group_name.get("external")
idoverrideuser = group_name.get("idoverrideuser")
posix = group_name.get("posix")
# Check mutually exclusive condition for multiple groups
# creation. It's not possible to check it with
# `mutually_exclusive` argument in `IPAAnsibleModule` class
# because it accepts only (list[str] or list[list[str]]). Here
# we need to loop over all groups and fail on mutually
# exclusive ones.
if all((posix, nonposix)) or\
all((posix, external)) or\
all((nonposix, external)):
ansible_module.fail_json(
msg="parameters are mutually exclusive for group "
"`{0}`: posix|nonposix|external".format(name))
# Duplicating the condition for multiple group creation
if external is False:
ansible_module.fail_json(
msg="group can not be non-external")
# If nonposix is used, set posix as not nonposix
if nonposix is not None:
posix = not nonposix
user = group_name.get("user")
group = group_name.get("group")
service = group_name.get("service")
membermanager_user = group_name.get("membermanager_user")
membermanager_group = group_name.get("membermanager_group")
externalmember = group_name.get("externalmember")
nomembers = group_name.get("nomembers")
check_parameters(ansible_module, state, action)
elif isinstance(group_name, (str, unicode)):
name = group_name
else:
ansible_module.fail_json(msg="Group '%s' is not valid" %
repr(group_name))
for name in names:
# Make sure group exists
res_find = find_group(ansible_module, name)
@@ -593,10 +818,12 @@ def main():
del_member_args["service"] = service_del
if is_external_group(res_find):
add_member_args["ipaexternalmember"] = \
externalmember_add
del_member_args["ipaexternalmember"] = \
externalmember_del
if len(externalmember_add) > 0:
add_member_args["ipaexternalmember"] = \
externalmember_add
if len(externalmember_del) > 0:
del_member_args["ipaexternalmember"] = \
externalmember_del
elif externalmember or external:
ansible_module.fail_json(
msg="Cannot add external members to a "

View File

@@ -44,7 +44,7 @@ options:
aliases: ["fqdn"]
required: false
hosts:
description: The list of user host dicts
description: The list of host dicts
required: false
type: list
elements: dict
@@ -441,6 +441,15 @@ EXAMPLES = """
description: Example host
force: yes
# Ensure multiple hosts are present with random passwords
- ipahost:
ipaadmin_password: SomeADMINpassword
hosts:
- name: host01.example.com
random: yes
- name: host02.example.com
random: yes
# Initiate generation of a random password for the host
- ipahost:
ipaadmin_password: SomeADMINpassword
@@ -449,6 +458,18 @@ EXAMPLES = """
ip_address: 192.168.0.123
random: yes
# Ensure multiple hosts are present with principals
- ipahost:
ipaadmin_password: SomeADMINpassword
hosts:
- name: host01.example.com
principal:
- host/testhost01.example.com
- name: host02.example.com
principal:
- host/myhost01.example.com
action: member
# Ensure host is disabled
- ipahost:
ipaadmin_password: SomeADMINpassword
@@ -466,16 +487,18 @@ EXAMPLES = """
RETURN = """
host:
description: Host dict with random password
returned: If random is yes and user did not exist or update_password is yes
returned: If random is yes and host did not exist or update_password is yes
type: dict
contains:
randompassword:
description: The generated random password
type: str
returned: If only one user is handled by the module
returned: |
If only one host is handled by the module without using hosts parameter
name:
description: The user name of the user that got a new random password
returned: If several users are handled by the module
description: The host name of the host that got a new random password
returned: |
If several hosts are handled by the module with the hosts parameter
type: dict
contains:
randompassword:
@@ -646,10 +669,10 @@ def check_parameters( # pylint: disable=unused-argument
# pylint: disable=unused-argument
def result_handler(module, result, command, name, args, errors, exit_args,
one_name):
single_host):
if "random" in args and command in ["host_add", "host_mod"] \
and "randompassword" in result["result"]:
if one_name:
if single_host:
exit_args["randompassword"] = \
result["result"]["randompassword"]
else:
@@ -671,7 +694,7 @@ def result_handler(module, result, command, name, args, errors, exit_args,
# pylint: disable=unused-argument
def exception_handler(module, ex, errors, exit_args, one_name):
def exception_handler(module, ex, errors, exit_args, single_host):
msg = str(ex)
if "already contains" in msg \
or "does not contain" in msg:
@@ -1468,7 +1491,7 @@ def main():
changed = ansible_module.execute_ipa_commands(
commands, result_handler, exception_handler,
exit_args=exit_args, one_name=len(names) == 1)
exit_args=exit_args, single_host=hosts is None)
# Done

View File

@@ -93,10 +93,12 @@ options:
action:
description: Work on netgroup or member level
required: false
type: str
default: netgroup
choices: ["member", "netgroup"]
state:
description: The state to ensure.
type: str
choices: ["present", "absent"]
default: present
author:
@@ -157,18 +159,29 @@ RETURN = """
from ansible.module_utils.ansible_freeipa_module import \
IPAAnsibleModule, compare_args_ipa, gen_add_del_lists, \
gen_add_list, gen_intersection_list, ipalib_errors, ensure_fqdn
gen_add_list, gen_intersection_list, ensure_fqdn
def find_netgroup(module, name):
"""Find if a netgroup with the given name already exist."""
try:
_result = module.ipa_command("netgroup_show", name, {"all": True})
except ipalib_errors.NotFound:
# An exception is raised if netgroup name is not found.
return None
else:
return _result["result"]
_args = {
"all": True,
"cn": name,
}
# `netgroup_find` is used here instead of `netgroup_show` to workaround
# FreeIPA bug https://pagure.io/freeipa/issue/9284.
# `ipa netgroup-show hostgroup` shows hostgroup - it's a bug.
# `ipa netgroup-find hostgroup` doesn't show hostgroup - it's correct.
_result = module.ipa_command("netgroup_find", name, _args)
if len(_result["result"]) > 1:
module.fail_json(
msg="There is more than one netgroup '%s'" % name)
elif len(_result["result"]) == 1:
return _result["result"][0]
return None
def gen_args(description, nisdomain, nomembers):

View File

@@ -46,82 +46,82 @@ options:
aliases: ["cn"]
maxlife:
description: Maximum password lifetime (in days)
type: int
type: str
required: false
aliases: ["krbmaxpwdlife"]
minlife:
description: Minimum password lifetime (in hours)
type: int
type: str
required: false
aliases: ["krbminpwdlife"]
history:
description: Password history size
type: int
type: str
required: false
aliases: ["krbpwdhistorylength"]
minclasses:
description: Minimum number of character classes
type: int
type: str
required: false
aliases: ["krbpwdmindiffchars"]
minlength:
description: Minimum length of password
type: int
type: str
required: false
aliases: ["krbpwdminlength"]
priority:
description: Priority of the policy (higher number means lower priority)
type: int
type: str
required: false
aliases: ["cospriority"]
maxfail:
description: Consecutive failures before lockout
type: int
type: str
required: false
aliases: ["krbpwdmaxfailure"]
failinterval:
description: Period after which failure count will be reset (seconds)
type: int
type: str
required: false
aliases: ["krbpwdfailurecountinterval"]
lockouttime:
description: Period for which lockout is enforced (seconds)
type: int
type: str
required: false
aliases: ["krbpwdlockoutduration"]
maxrepeat:
description: >
Maximum number of same consecutive characters.
Requires IPA 4.9+
type: int
type: str
required: false
aliases: ["ipapwdmaxrepeat"]
maxsequence:
description: >
The maximum length of monotonic character sequences (abcd).
Requires IPA 4.9+
type: int
type: str
required: false
aliases: ["ipapwdmaxsequence"]
dictcheck:
description: >
Check if the password is a dictionary word.
Requires IPA 4.9+
type: bool
type: str
required: false
aliases: ["ipapwdictcheck"]
usercheck:
description: >
Check if the password contains the username.
Requires IPA 4.9+
type: bool
type: str
required: false
aliases: ["ipapwdusercheck"]
gracelimit:
description: >
Number of LDAP authentications allowed after expiration.
Requires IPA 4.10.1+
type: int
type: str
required: false
aliases: ["passwordgracelimit"]
state:
@@ -171,7 +171,8 @@ def find_pwpolicy(module, name):
return None
def gen_args(maxlife, minlife, history, minclasses, minlength, priority,
def gen_args(module,
maxlife, minlife, history, minclasses, minlength, priority,
maxfail, failinterval, lockouttime, maxrepeat, maxsequence,
dictcheck, usercheck, gracelimit):
_args = {}
@@ -198,9 +199,19 @@ def gen_args(maxlife, minlife, history, minclasses, minlength, priority,
if maxsequence is not None:
_args["ipapwdmaxrsequence"] = maxsequence
if dictcheck is not None:
_args["ipapwddictcheck"] = dictcheck
if module.ipa_check_version("<", "4.9.10"):
# Allowed values: "TRUE", "FALSE", ""
_args["ipapwddictcheck"] = "TRUE" if dictcheck is True else \
"FALSE" if dictcheck is False else dictcheck
else:
_args["ipapwddictcheck"] = dictcheck
if usercheck is not None:
_args["ipapwdusercheck"] = usercheck
if module.ipa_check_version("<", "4.9.10"):
# Allowed values: "TRUE", "FALSE", ""
_args["ipapwdusercheck"] = "TRUE" if usercheck is True else \
"FALSE" if usercheck is False else usercheck
else:
_args["ipapwdusercheck"] = usercheck
if gracelimit is not None:
_args["passwordgracelimit"] = gracelimit
@@ -242,31 +253,31 @@ def main():
default=None, required=False),
# present
maxlife=dict(type="int", aliases=["krbmaxpwdlife"], default=None),
minlife=dict(type="int", aliases=["krbminpwdlife"], default=None),
history=dict(type="int", aliases=["krbpwdhistorylength"],
maxlife=dict(type="str", aliases=["krbmaxpwdlife"], default=None),
minlife=dict(type="str", aliases=["krbminpwdlife"], default=None),
history=dict(type="str", aliases=["krbpwdhistorylength"],
default=None),
minclasses=dict(type="int", aliases=["krbpwdmindiffchars"],
minclasses=dict(type="str", aliases=["krbpwdmindiffchars"],
default=None),
minlength=dict(type="int", aliases=["krbpwdminlength"],
minlength=dict(type="str", aliases=["krbpwdminlength"],
default=None),
priority=dict(type="int", aliases=["cospriority"], default=None),
maxfail=dict(type="int", aliases=["krbpwdmaxfailure"],
priority=dict(type="str", aliases=["cospriority"], default=None),
maxfail=dict(type="str", aliases=["krbpwdmaxfailure"],
default=None),
failinterval=dict(type="int",
failinterval=dict(type="str",
aliases=["krbpwdfailurecountinterval"],
default=None),
lockouttime=dict(type="int", aliases=["krbpwdlockoutduration"],
lockouttime=dict(type="str", aliases=["krbpwdlockoutduration"],
default=None),
maxrepeat=dict(type="int", aliases=["ipapwdmaxrepeat"],
maxrepeat=dict(type="str", aliases=["ipapwdmaxrepeat"],
default=None),
maxsequence=dict(type="int", aliases=["ipapwdmaxsequence"],
maxsequence=dict(type="str", aliases=["ipapwdmaxsequence"],
default=None),
dictcheck=dict(type="bool", aliases=["ipapwdictcheck"],
dictcheck=dict(type="str", aliases=["ipapwdictcheck"],
default=None),
usercheck=dict(type="bool", aliases=["ipapwusercheck"],
usercheck=dict(type="str", aliases=["ipapwdusercheck"],
default=None),
gracelimit=dict(type="int", aliases=["passwordgracelimit"],
gracelimit=dict(type="str", aliases=["passwordgracelimit"],
default=None),
# state
state=dict(type="str", default="present",
@@ -325,7 +336,48 @@ def main():
ansible_module.params_fail_used_invalid(invalid, state)
if gracelimit is not None:
# Ensure parameter values are valid and have proper type.
def int_or_empty_param(value, param):
if value is not None and value != "":
try:
value = int(value)
except ValueError:
ansible_module.fail_json(
msg="Invalid value '%s' for argument '%s'" % (value, param)
)
return value
maxlife = int_or_empty_param(maxlife, "maxlife")
minlife = int_or_empty_param(minlife, "minlife")
history = int_or_empty_param(history, "history")
minclasses = int_or_empty_param(minclasses, "minclasses")
minlength = int_or_empty_param(minlength, "minlength")
priority = int_or_empty_param(priority, "priority")
maxfail = int_or_empty_param(maxfail, "maxfail")
failinterval = int_or_empty_param(failinterval, "failinterval")
lockouttime = int_or_empty_param(lockouttime, "lockouttime")
maxrepeat = int_or_empty_param(maxrepeat, "maxrepeat")
maxsequence = int_or_empty_param(maxsequence, "maxsequence")
gracelimit = int_or_empty_param(gracelimit, "gracelimit")
def bool_or_empty_param(value, param): # pylint: disable=R1710
# As of Ansible 2.14, values True, False, Yes an No, with variable
# capitalization are accepted by Ansible.
if not value:
return value
if value in ["TRUE", "True", "true", "YES", "Yes", "yes"]:
return True
if value in ["FALSE", "False", "false", "NO", "No", "no"]:
return False
ansible_module.fail_json(
msg="Invalid value '%s' for argument '%s'." % (value, param)
)
dictcheck = bool_or_empty_param(dictcheck, "dictcheck")
usercheck = bool_or_empty_param(usercheck, "usercheck")
# Ensure gracelimit has proper limit.
if gracelimit:
if gracelimit < -1:
ansible_module.fail_json(
msg="'gracelimit' must be no less than -1")
@@ -351,7 +403,8 @@ def main():
# Create command
if state == "present":
# Generate args
args = gen_args(maxlife, minlife, history, minclasses,
args = gen_args(ansible_module,
maxlife, minlife, history, minclasses,
minlength, priority, maxfail, failinterval,
lockouttime, maxrepeat, maxsequence, dictcheck,
usercheck, gracelimit)

View File

@@ -124,12 +124,12 @@ options:
required: false
type: bool
uid:
description: The UID
description: User ID Number (system will assign one if not provided)
type: int
required: false
aliases: ["uidnumber"]
gid:
description: The GID
description: Group ID Number
type: int
required: false
aliases: ["gidnumber"]
@@ -348,12 +348,12 @@ options:
required: false
type: bool
uid:
description: The UID
description: User ID Number (system will assign one if not provided)
type: int
required: false
aliases: ["uidnumber"]
gid:
description: The GID
description: Group ID Number
type: int
required: false
aliases: ["gidnumber"]
@@ -548,6 +548,17 @@ EXAMPLES = """
first: brain
last: Acme
# Create multiple users pinky and brain
- ipauser:
ipaadmin_password: SomeADMINpassword
users:
- name: pinky
first: pinky
last: Acme
- name: brain
first: brain
last: Acme
# Delete user pinky, but preserved
- ipauser:
ipaadmin_password: SomeADMINpassword
@@ -573,6 +584,14 @@ EXAMPLES = """
name: pinky,brain
state: enabled
# Remove but preserve user pinky
- ipauser:
ipaadmin_password: SomeADMINpassword
users:
- name: pinky
preserve: yes
state: absent
# Remove user pinky and brain
- ipauser:
ipaadmin_password: SomeADMINpassword
@@ -589,10 +608,12 @@ user:
randompassword:
description: The generated random password
type: str
returned: If only one user is handled by the module
returned: |
If only one user is handled by the module without using users parameter
name:
description: The user name of the user that got a new random password
returned: If several users are handled by the module
returned: |
If several users are handled by the module with the users parameter
type: dict
contains:
randompassword:
@@ -834,11 +855,11 @@ def gen_certmapdata_args(certmapdata):
# pylint: disable=unused-argument
def result_handler(module, result, command, name, args, errors, exit_args,
one_name):
single_user):
if "random" in args and command in ["user_add", "user_mod"] \
and "randompassword" in result["result"]:
if one_name:
if single_user:
exit_args["randompassword"] = \
result["result"]["randompassword"]
else:
@@ -861,7 +882,7 @@ def result_handler(module, result, command, name, args, errors, exit_args,
# pylint: disable=unused-argument
def exception_handler(module, ex, errors, exit_args, one_name):
def exception_handler(module, ex, errors, exit_args, single_user):
msg = str(ex)
if "already contains" in msg \
or "does not contain" in msg:
@@ -1511,7 +1532,7 @@ def main():
changed = ansible_module.execute_ipa_commands(
commands, result_handler, exception_handler,
exit_args=exit_args, one_name=len(names) == 1)
exit_args=exit_args, single_user=users is None)
# Done
ansible_module.exit_json(changed=changed, user=exit_args)

View File

@@ -1,10 +1,10 @@
-r requirements-tests.txt
ipdb==0.13.4
pre-commit
flake8==4.0.1
pre-commit==2.20.0
flake8==5.0.3
flake8-bugbear==22.10.27
pylint==2.13.7
wrapt >= 1.14.0
pylint==2.14.4
wrapt == 1.14.0
pydocstyle==6.0.0
yamllint==1.26.3
ansible-lint==5.3.2
yamllint==1.28.0
ansible-lint==6.6.1

View File

@@ -1,7 +1,8 @@
-r requirements.txt
pytest>=2.7
pytest-sourceorder>=0.5
pytest==7.1.3
pytest-sourceorder==0.6.0
pytest-split>=0.8.0
pytest-custom_exit_code>=0.3.0
pytest-testinfra>=5.0
pytest-testinfra==6.8.0
pytest-randomly==3.12.0
pyyaml>=3

View File

@@ -6,15 +6,15 @@ galaxy_info:
description: A role to backup and restore an IPA server
company: Red Hat, Inc
license: GPLv3
min_ansible_version: 2.8
min_ansible_version: "2.8"
platforms:
- name: Fedora
versions:
- all
- name: EL
versions:
- 7
- 8
- "7"
- "8"
galaxy_tags:
- identity
- ipa

View File

@@ -2,20 +2,22 @@
# tasks file for ipabackup
- name: Create backup
shell: >
ansible.builtin.shell: >
ipa-backup
{{ "--gpg" if ipabackup_gpg | bool else "" }}
{{ "--gpg-keyring="+ipabackup_gpg_keyring if ipabackup_gpg_keyring is defined else "" }}
{{ "--gpg-keyring=" + ipabackup_gpg_keyring if ipabackup_gpg_keyring is defined else "" }}
{{ "--data" if ipabackup_data | bool else "" }}
{{ "--logs" if ipabackup_logs | bool else "" }}
{{ "--online" if ipabackup_online | bool else "" }}
{{ "--disable-role-check" if ipabackup_disable_role_check | bool else "" }}
{{ "--log-file="+ipabackup_log_file if ipabackup_log_file is defined else "" }}
{{ "--log-file=" + ipabackup_log_file if ipabackup_log_file is defined else "" }}
register: result_ipabackup
- block:
- name: Handle backup
when: ipabackup_to_controller
block:
- name: Get ipabackup_item from stderr or stdout output
set_fact:
ansible.builtin.set_fact:
ipabackup_item: "{{ item | regex_search('\n.*/([^\n]+)','\\1') | first }}"
when: item.find("Backed up to "+ipabackup_dir+"/") > 0
with_items:
@@ -25,15 +27,14 @@
label: ""
- name: Fail on missing ipabackup_item
fail: msg="Failed to get ipabackup_item"
ansible.builtin.fail:
msg: "Failed to get ipabackup_item"
when: ipabackup_item is not defined
- name: Copy backup to controller
include_tasks: "{{ role_path }}/tasks/copy_backup_from_server.yml"
ansible.builtin.include_tasks: "{{ role_path }}/tasks/copy_backup_from_server.yml"
when: state|default("present") == "present"
- name: Remove backup on server
include_tasks: "{{ role_path }}/tasks/remove_backup_from_server.yml"
ansible.builtin.include_tasks: "{{ role_path }}/tasks/remove_backup_from_server.yml"
when: not ipabackup_keep_on_server
when: ipabackup_to_controller

View File

@@ -1,45 +1,47 @@
---
- name: Fail on invalid ipabackup_item
fail: msg="ipabackup_item {{ ipabackup_item }} is not valid"
ansible.builtin.fail:
msg: "ipabackup_item {{ ipabackup_item }} is not valid"
when: ipabackup_item is not defined or
ipabackup_item | length < 1 or
(ipabackup_item.find("ipa-full-") == -1 and
ipabackup_item.find("ipa-data-") == -1)
- name: Set controller destination directory
set_fact:
ipabackup_controller_dir:
"{{ ipabackup_controller_path | default(lookup('env','PWD')) }}/{{
ansible.builtin.set_fact:
__derived_controller_dir:
"{{ ipabackup_controller_path | default(lookup('env', 'PWD')) }}/{{
ipabackup_name_prefix | default(ansible_facts['fqdn']) }}_{{
ipabackup_item }}/"
- name: Stat backup on server
stat:
ansible.builtin.stat:
path: "{{ ipabackup_dir }}/{{ ipabackup_item }}"
register: result_backup_stat
- name: Fail on missing backup directory
fail: msg="Unable to find backup {{ ipabackup_item }}"
ansible.builtin.fail:
msg: "Unable to find backup {{ ipabackup_item }}"
when: result_backup_stat.stat.isdir is not defined
- name: Get backup files to copy for "{{ ipabackup_item }}"
shell:
ansible.builtin.shell:
find . -type f | cut -d"/" -f 2
args:
chdir: "{{ ipabackup_dir }}/{{ ipabackup_item }}"
register: result_find_backup_files
- name: Copy server backup files to controller
fetch:
ansible.builtin.fetch:
flat: yes
src: "{{ ipabackup_dir }}/{{ ipabackup_item }}/{{ item }}"
dest: "{{ ipabackup_controller_dir }}"
dest: "{{ __derived_controller_dir }}"
with_items:
- "{{ result_find_backup_files.stdout_lines }}"
- name: Fix file modes for backup on controller
file:
dest: "{{ ipabackup_controller_dir }}"
ansible.builtin.file:
dest: "{{ __derived_controller_dir }}"
mode: u=rwX,go=
recurse: yes
delegate_to: localhost

View File

@@ -1,41 +1,43 @@
---
- name: Fail on invalid ipabackup_name
fail: msg="ipabackup_name {{ ipabackup_name }} is not valid"
ansible.builtin.fail:
msg: "ipabackup_name {{ ipabackup_name }} is not valid"
when: ipabackup_name is not defined or
ipabackup_name | length < 1 or
(ipabackup_name.find("ipa-full-") == -1 and
ipabackup_name.find("ipa-data-") == -1)
- name: Set controller source directory
set_fact:
ipabackup_controller_dir:
"{{ ipabackup_controller_path | default(lookup('env','PWD')) }}"
ansible.builtin.set_fact:
__derived_controller_dir:
"{{ ipabackup_controller_path | default(lookup('env', 'PWD')) }}"
- name: Set ipabackup_item
set_fact:
ansible.builtin.set_fact:
ipabackup_item:
"{{ ipabackup_name | regex_search('.*_(ipa-.+)','\\1') | first }}"
"{{ ipabackup_name | regex_search('.*_(ipa-.+)', '\\1') | first }}"
when: "'_ipa-' in ipabackup_name"
- name: Set ipabackup_item
set_fact:
ansible.builtin.set_fact:
ipabackup_item: "{{ ipabackup_name }}"
when: "'_ipa-' not in ipabackup_name"
- name: Stat backup to copy
stat:
path: "{{ ipabackup_controller_dir }}/{{ ipabackup_name }}"
ansible.builtin.stat:
path: "{{ __derived_controller_dir }}/{{ ipabackup_name }}"
register: result_backup_stat
delegate_to: localhost
become: no
- name: Fail on missing backup to copy
fail: msg="Unable to find backup {{ ipabackup_name }}"
ansible.builtin.fail:
msg: "Unable to find backup {{ ipabackup_name }}"
when: result_backup_stat.stat.isdir is not defined
- name: Copy backup files to server for "{{ ipabackup_item }}"
copy:
src: "{{ ipabackup_controller_dir }}/{{ ipabackup_name }}/"
ansible.builtin.copy:
src: "{{ __derived_controller_dir }}/{{ ipabackup_name }}/"
dest: "{{ ipabackup_dir }}/{{ ipabackup_item }}"
owner: root
group: root

View File

@@ -4,5 +4,5 @@
register: result_ipabackup_get_backup_dir
- name: Set IPA backup dir
set_fact:
ansible.builtin.set_fact:
ipabackup_dir: "{{ result_ipabackup_get_backup_dir.backup_dir }}"

View File

@@ -2,7 +2,8 @@
# tasks file for ipabackup
- name: Check for empty vars
fail: msg="Variable {{ item }} is empty"
ansible.builtin.fail:
msg: "Variable {{ item }} is empty"
when: "item in vars and not vars[item]"
with_items: "{{ ipabackup_empty_var_checks }}"
vars:
@@ -18,74 +19,82 @@
- ipabackup_firewalld_zone
- name: Set ipabackup_data if ipabackup_data is not set but ipabackup_online is
set_fact:
ansible.builtin.set_fact:
ipabackup_data: yes
when: ipabackup_online | bool and not ipabackup_data | bool
- name: Fail if ipabackup_from_controller and ipabackup_to_controller are set
fail: msg="ipabackup_from_controller and ipabackup_to_controller are set"
ansible.builtin.fail:
msg: "ipabackup_from_controller and ipabackup_to_controller are set"
when: ipabackup_from_controller | bool and ipabackup_to_controller | bool
- name: Fail for given ipabackup_name if state is not copied, restored or absent
fail: msg="ipabackup_name is given and state is not copied, restored or absent"
ansible.builtin.fail:
msg: "ipabackup_name is given and state is not copied, restored or absent"
when: state is not defined or
(state != "copied" and state != "restored" and state != "absent") and
ipabackup_name is defined
- name: Get ipabackup_dir from IPA installation
include_tasks: "{{ role_path }}/tasks/get_ipabackup_dir.yml"
ansible.builtin.include_tasks: "{{ role_path }}/tasks/get_ipabackup_dir.yml"
- name: Backup IPA server
include_tasks: "{{ role_path }}/tasks/backup.yml"
ansible.builtin.include_tasks: "{{ role_path }}/tasks/backup.yml"
when: state|default("present") == "present"
- name: Fail on missing ipabackup_name
fail: msg="ipabackup_name is not set"
ansible.builtin.fail:
msg: "ipabackup_name is not set"
when: (ipabackup_name is not defined or not ipabackup_name) and
state is defined and
(state == "copied" or state == "restored" or state == "absent")
- block:
- name: Get all backup names for copy to controller
when: state is defined and
((state == "copied" and ipabackup_to_controller) or
state == "absent") and
ipabackup_name is defined and ipabackup_name == "all"
block:
- name: Get list of all backups on IPA server
shell:
ansible.builtin.shell:
find . -name "ipa-full-*" -o -name "ipa-data-*" | cut -d"/" -f 2
args:
chdir: "{{ ipabackup_dir }}/"
register: result_backup_find_backup_files
- name: Set ipabackup_names using backup list
set_fact:
ansible.builtin.set_fact:
ipabackup_names: "{{ result_backup_find_backup_files.stdout_lines }}"
when: state is defined and
((state == "copied" and ipabackup_to_controller) or
state == "absent") and
ipabackup_name is defined and ipabackup_name == "all"
- block:
- name: Set ipabackup_names from ipabackup_name
when: ipabackup_names is not defined and ipabackup_name is defined
block:
- name: Fail on ipabackup_name all
fail: msg="ipabackup_name can not be all in this case"
ansible.builtin.fail:
msg: "ipabackup_name can not be all in this case"
when: ipabackup_name is defined and ipabackup_name == "all"
- name: Set ipabackup_names from ipabackup_name string
set_fact:
ansible.builtin.set_fact:
ipabackup_names: ["{{ ipabackup_name }}"]
when: ipabackup_name | type_debug != "list"
- name: Set ipabackup_names from ipabackup_name list
set_fact:
ansible.builtin.set_fact:
ipabackup_names: "{{ ipabackup_name }}"
when: ipabackup_name | type_debug == "list"
when: ipabackup_names is not defined and ipabackup_name is defined
- name: Set empty ipabackup_names if ipabackup_name is not defined
set_fact:
ansible.builtin.set_fact:
ipabackup_names: []
when: ipabackup_names is not defined and ipabackup_name is not defined
- block:
- name: Process "{{ ipabackup_names }}"
when: state is defined and
((state == "copied" and ipabackup_to_controller) or state == "absent")
block:
- name: Copy backup from IPA server
include_tasks: "{{ role_path }}/tasks/copy_backup_from_server.yml"
ansible.builtin.include_tasks: "{{ role_path }}/tasks/copy_backup_from_server.yml"
vars:
ipabackup_item: "{{ main_item | basename }}"
with_items:
@@ -95,7 +104,7 @@
when: state is defined and state == "copied"
- name: Remove backup from IPA server
include_tasks: "{{ role_path }}/tasks/remove_backup_from_server.yml"
ansible.builtin.include_tasks: "{{ role_path }}/tasks/remove_backup_from_server.yml"
vars:
ipabackup_item: "{{ main_item | basename }}"
with_items:
@@ -104,34 +113,32 @@
loop_var: main_item
when: state is defined and state == "absent"
when: state is defined and
((state == "copied" and ipabackup_to_controller) or state == "absent")
# Fail with more than one entry in ipabackup_names for copy to sever and
# restore.
- name: Fail to copy or restore more than one backup on the server
fail: msg="Only one backup can be copied to the server or restored"
ansible.builtin.fail:
msg: "Only one backup can be copied to the server or restored"
when: state is defined and (state == "copied" or state == "restored") and
ipabackup_from_controller | bool and ipabackup_names | length != 1
# Use only first item in ipabackup_names for copy to server and for restore.
- block:
- name: Copy backup to server
include_tasks: "{{ role_path }}/tasks/copy_backup_to_server.yml"
- name: Restore IPA server after copy
include_tasks: "{{ role_path }}/tasks/restore.yml"
when: state|default("present") == "restored"
vars:
ipabackup_name: "{{ ipabackup_names[0] }}"
- name: Process "{{ ipabackup_names[0] }}"
when: ipabackup_from_controller or
(state|default("present") == "copied" and not ipabackup_to_controller)
vars:
ipabackup_name: "{{ ipabackup_names[0] }}"
block:
- name: Copy backup to server
ansible.builtin.include_tasks: "{{ role_path }}/tasks/copy_backup_to_server.yml"
- name: Restore IPA server after copy
ansible.builtin.include_tasks: "{{ role_path }}/tasks/restore.yml"
when: state|default("present") == "restored"
- name: Restore IPA server
include_tasks: "{{ role_path }}/tasks/restore.yml"
ansible.builtin.include_tasks: "{{ role_path }}/tasks/restore.yml"
vars:
ipabackup_item: "{{ ipabackup_names[0] | basename }}"
when: not ipabackup_from_controller and

View File

@@ -1,5 +1,5 @@
---
- name: Remove backup "{{ ipabackup_item }}"
file:
ansible.builtin.file:
path: "{{ ipabackup_dir }}/{{ ipabackup_item }}"
state: absent

View File

@@ -4,7 +4,7 @@
### VARIABLES
- name: Import variables specific to distribution
include_vars: "{{ item }}"
ansible.builtin.include_vars: "{{ item }}"
with_first_found:
- "{{ role_path }}/vars/{{ ansible_facts['distribution'] }}-{{ ansible_facts['distribution_version'] }}.yml"
- "{{ role_path }}/vars/{{ ansible_facts['distribution'] }}-{{ ansible_facts['distribution_major_version'] }}.yml"
@@ -21,30 +21,32 @@
### GET SERVICES FROM BACKUP
- name: Stat backup on server
stat:
ansible.builtin.stat:
path: "{{ ipabackup_dir }}/{{ ipabackup_item }}"
register: result_backup_stat
- name: Fail on missing backup directory
fail: msg="Unable to find backup {{ ipabackup_item }}"
ansible.builtin.fail:
msg: "Unable to find backup {{ ipabackup_item }}"
when: result_backup_stat.stat.isdir is not defined
- name: Stat header file in backup "{{ ipabackup_item }}"
stat:
ansible.builtin.stat:
path: "{{ ipabackup_dir }}/{{ ipabackup_item }}/header"
register: result_backup_header_stat
- name: Fail on missing header file in backup
fail: msg="Unable to find backup {{ ipabackup_item }} header file"
ansible.builtin.fail:
msg: "Unable to find backup {{ ipabackup_item }} header file"
when: result_backup_header_stat.stat.isreg is not defined
- name: Get services from backup
shell: >
ansible.builtin.shell: >
grep "^services = " "{{ ipabackup_dir }}/{{ ipabackup_item }}/header" | cut -d"=" -f2 | tr -d '[:space:]'
register: result_services_grep
- name: Set ipabackup_services
set_fact:
ansible.builtin.set_fact:
ipabackup_services: "{{ result_services_grep.stdout.split(',') }}"
ipabackup_service_dns: DNS
ipabackup_service_adtrust: ADTRUST
@@ -52,78 +54,78 @@
### INSTALL PACKAGES
- block:
- name: Package installation
when: ipabackup_install_packages | bool
block:
- name: Ensure that IPA server packages are installed
package:
ansible.builtin.package:
name: "{{ ipaserver_packages }}"
state: present
- name: Ensure that IPA server packages for dns are installed
package:
ansible.builtin.package:
name: "{{ ipaserver_packages_dns }}"
state: present
when: ipabackup_service_dns in ipabackup_services
- name: Ensure that IPA server packages for adtrust are installed
package:
ansible.builtin.package:
name: "{{ ipaserver_packages_adtrust }}"
state: present
when: ipabackup_service_adtrust in ipabackup_services
- name: Ensure that firewalld packages are installed
package:
ansible.builtin.package:
name: "{{ ipaserver_packages_firewalld }}"
state: present
when: ipabackup_setup_firewalld | bool
when: ipabackup_install_packages | bool
### START FIREWALLD
- block:
- name: Firewall configuration
when: ipabackup_setup_firewalld | bool
block:
- name: Ensure that firewalld is running
systemd:
ansible.builtin.systemd:
name: firewalld
enabled: yes
state: started
- name: Firewalld - Verify runtime zone "{{ ipabackup_firewalld_zone }}"
shell: >
ansible.builtin.shell: >
firewall-cmd
--info-zone="{{ ipabackup_firewalld_zone }}"
>/dev/null
when: ipabackup_firewalld_zone is defined
- name: Firewalld - Verify permanent zone "{{ ipabackup_firewalld_zone }}"
shell: >
ansible.builtin.shell: >
firewall-cmd
--permanent
--info-zone="{{ ipabackup_firewalld_zone }}"
>/dev/null
when: ipabackup_firewalld_zone is defined
when: ipabackup_setup_firewalld | bool
### RESTORE
- name: Restore backup
no_log: True
shell: >
ansible.builtin.shell: >
ipa-restore
{{ ipabackup_item }}
--unattended
{{ "--password="+ipabackup_password if ipabackup_password is defined else "" }}
{{ "--password=" + ipabackup_password if ipabackup_password is defined else "" }}
{{ "--data" if ipabackup_data | bool else "" }}
{{ "--online" if ipabackup_online | bool else "" }}
{{ "--instance="+ipabackup_instance if ipabackup_instance is defined else "" }}
{{ "--backend="+ipabackup_backend if ipabackup_backend is defined else "" }}
{{ "--instance=" + ipabackup_instance if ipabackup_instance is defined else "" }}
{{ "--backend=" + ipabackup_backend if ipabackup_backend is defined else "" }}
{{ "--no-logs" if ipabackup_no_logs | bool else "" }}
{{ "--log-file="+ipabackup_log_file if ipabackup_log_file is defined else "" }}
{{ "--log-file=" + ipabackup_log_file if ipabackup_log_file is defined else "" }}
register: result_iparestore
ignore_errors: yes
- name: Report error for restore operation
debug:
ansible.builtin.debug:
msg: "{{ result_iparestore.stderr }}"
when: result_iparestore is failed
failed_when: yes
@@ -131,10 +133,10 @@
### CONFIGURE FIREWALLD
- name: Configure firewalld
command: >
ansible.builtin.command: >
firewall-cmd
--permanent
{{ "--zone="+ipabackup_firewalld_zone if ipabackup_firewalld_zone is defined else "" }}
{{ "--zone=" + ipabackup_firewalld_zone if ipabackup_firewalld_zone is defined else "" }}
--add-service=freeipa-ldap
--add-service=freeipa-ldaps
{{ "--add-service=freeipa-trust" if ipabackup_service_adtrust in ipabackup_services else "" }}
@@ -143,9 +145,9 @@
when: ipabackup_setup_firewalld | bool
- name: Configure firewalld runtime
command: >
ansible.builtin.command: >
firewall-cmd
{{ "--zone="+ipabackup_firewalld_zone if ipabackup_firewalld_zone is defined else "" }}
{{ "--zone=" + ipabackup_firewalld_zone if ipabackup_firewalld_zone is defined else "" }}
--add-service=freeipa-ldap
--add-service=freeipa-ldaps
{{ "--add-service=freeipa-trust" if ipabackup_service_adtrust in ipabackup_services else "" }}

View File

@@ -183,6 +183,7 @@ Variable | Description | Required
`ipaclient_no_ssh` | The bool value defines if OpenSSH client will be configured. `ipaclient_no_ssh` defaults to `no`. | no
`ipaclient_no_sshd` | The bool value defines if OpenSSH server will be configured. `ipaclient_no_sshd` defaults to `no`. | no
`ipaclient_no_sudo` | The bool value defines if SSSD will be configured as a data source for sudo. `ipaclient_no_sudo` defaults to `no`. | no
`ipaclient_subid` | The bool value defines if SSSD will be configured as a data source for subid. `ipaclient_subid` defaults to `no`. | no
`ipaclient_no_dns_sshfp` | The bool value defines if DNS SSHFP records will not be created automatically. `ipaclient_no_dns_sshfp` defaults to `no`. | no
`ipaclient_force` | The bool value defines if settings will be forced even in the error case. `ipaclient_force` defaults to `no`. | no
`ipaclient_force_ntpd` | The bool value defines if ntpd usage will be forced. This is not supported anymore and leads to a warning. `ipaclient_force_ntpd` defaults to `no`. | no

View File

@@ -13,6 +13,7 @@ ipaclient_ssh_trust_dns: no
ipaclient_no_ssh: no
ipaclient_no_sshd: no
ipaclient_no_sudo: no
ipaclient_subid: no
ipaclient_no_dns_sshfp: no
ipaclient_force: no
ipaclient_force_ntpd: no

View File

@@ -55,6 +55,10 @@ options:
type: bool
required: no
default: no
krb_name:
description: The krb5 config file name
type: str
required: yes
author:
- Thomas Woerner (@t-woerner)
'''
@@ -65,6 +69,7 @@ EXAMPLES = '''
servers: ["server1.example.com","server2.example.com"]
domain: example.com
hostname: client1.example.com
krb_name: /tmp/tmpkrb5.conf
register: result_ipaclient_api
'''
@@ -99,6 +104,7 @@ def main():
realm=dict(required=True, type='str'),
hostname=dict(required=True, type='str'),
debug=dict(required=False, type='bool', default="false"),
krb_name=dict(required=True, type='str'),
),
supports_check_mode=False,
)
@@ -110,9 +116,11 @@ def main():
realm = module.params.get('realm')
hostname = module.params.get('hostname')
debug = module.params.get('debug')
krb_name = module.params.get('krb_name')
host_principal = 'host/%s@%s' % (hostname, realm)
os.environ['KRB5CCNAME'] = paths.IPA_DNS_CCACHE
os.environ['KRB5_CONFIG'] = krb_name
ca_certs = x509.load_certificate_list_from_file(paths.IPA_CA_CRT)
if 40500 <= NUM_VERSION < 40590:

View File

@@ -266,10 +266,8 @@ def unconfigure_dns_resolver(fstore=None):
def main():
module = AnsibleModule(
argument_spec=dict(
nameservers=dict(type="list", elements="str", aliases=["cn"],
required=False),
searchdomains=dict(type="list", elements="str", aliases=["cn"],
required=False),
nameservers=dict(type="list", elements="str", required=False),
searchdomains=dict(type="list", elements="str", required=False),
state=dict(type="str", default="present",
choices=["present", "absent"]),
),

View File

@@ -54,6 +54,10 @@ options:
the host entry will not be changed on the server
type: bool
required: yes
krb_name:
description: The krb5 config file name
type: str
required: yes
author:
- Thomas Woerner (@t-woerner)
'''
@@ -65,6 +69,7 @@ EXAMPLES = '''
realm: EXAMPLE.COM
basedn: dc=example,dc=com
allow_repair: yes
krb_name: /tmp/tmpkrb5.conf
'''
RETURN = '''
@@ -87,6 +92,7 @@ def main():
realm=dict(required=True, type='str'),
basedn=dict(required=True, type='str'),
allow_repair=dict(required=True, type='bool'),
krb_name=dict(required=True, type='str'),
),
)
@@ -98,6 +104,8 @@ def main():
realm = module.params.get('realm')
basedn = module.params.get('basedn')
allow_repair = module.params.get('allow_repair')
krb_name = module.params.get('krb_name')
os.environ['KRB5_CONFIG'] = krb_name
env = {'PATH': SECURE_PATH}
fstore = sysrestore.FileStore(paths.IPA_CLIENT_SYSRESTORE)

View File

@@ -46,10 +46,6 @@ options:
type: list
elements: str
required: yes
domain:
description: Primary DNS domain of the IPA deployment
type: str
required: yes
realm:
description: Kerberos realm name of the IPA deployment
type: str
@@ -58,10 +54,6 @@ options:
description: Fully qualified name of this host
type: str
required: yes
kdc:
description: The name or address of the host running the KDC
type: str
required: yes
basedn:
description: The basedn of the IPA server (of the form dc=example,dc=com)
type: str
@@ -102,6 +94,10 @@ options:
description: Turn on extra debugging
type: bool
required: no
krb_name:
description: The krb5 config file name
type: str
required: yes
author:
- Thomas Woerner (@t-woerner)
'''
@@ -111,27 +107,25 @@ EXAMPLES = '''
- name: Join IPA in force mode with maximum 5 kinit attempts
ipaclient_join:
servers: ["server1.example.com","server2.example.com"]
domain: example.com
realm: EXAMPLE.COM
kdc: server1.example.com
basedn: dc=example,dc=com
hostname: client1.example.com
principal: admin
password: MySecretPassword
force_join: yes
kinit_attempts: 5
krb_name: /tmp/tmpkrb5.conf
# Join IPA to get the keytab using ipadiscovery return values
- name: Join IPA
ipaclient_join:
servers: "{{ ipadiscovery.servers }}"
domain: "{{ ipadiscovery.domain }}"
realm: "{{ ipadiscovery.realm }}"
kdc: "{{ ipadiscovery.kdc }}"
basedn: "{{ ipadiscovery.basedn }}"
hostname: "{{ ipadiscovery.hostname }}"
principal: admin
password: MySecretPassword
krb_name: /tmp/tmpkrb5.conf
'''
RETURN = '''
@@ -147,9 +141,9 @@ import tempfile
from ansible.module_utils.basic import AnsibleModule
from ansible.module_utils.ansible_ipa_client import (
setup_logging, check_imports,
SECURE_PATH, sysrestore, paths, options, configure_krb5_conf,
realm_to_suffix, kinit_keytab, GSSError, kinit_password, NUM_VERSION,
get_ca_cert, get_ca_certs, errors, run
SECURE_PATH, sysrestore, paths, options, realm_to_suffix, kinit_keytab,
GSSError, kinit_password, NUM_VERSION, get_ca_cert, get_ca_certs, errors,
run
)
@@ -157,10 +151,8 @@ def main():
module = AnsibleModule(
argument_spec=dict(
servers=dict(required=True, type='list', elements='str'),
domain=dict(required=True, type='str'),
realm=dict(required=True, type='str'),
hostname=dict(required=True, type='str'),
kdc=dict(required=True, type='str'),
basedn=dict(required=True, type='str'),
principal=dict(required=False, type='str'),
password=dict(required=False, type='str', no_log=True),
@@ -170,6 +162,7 @@ def main():
force_join=dict(required=False, type='bool'),
kinit_attempts=dict(required=False, type='int', default=5),
debug=dict(required=False, type='bool'),
krb_name=dict(required=True, type='str'),
),
supports_check_mode=False,
)
@@ -179,11 +172,9 @@ def main():
setup_logging()
servers = module.params.get('servers')
domain = module.params.get('domain')
realm = module.params.get('realm')
hostname = module.params.get('hostname')
basedn = module.params.get('basedn')
kdc = module.params.get('kdc')
force_join = module.params.get('force_join')
principal = module.params.get('principal')
password = module.params.get('password')
@@ -192,6 +183,7 @@ def main():
ca_cert_file = module.params.get('ca_cert_file')
kinit_attempts = module.params.get('kinit_attempts')
debug = module.params.get('debug')
krb_name = module.params.get('krb_name')
if password is not None and keytab is not None:
module.fail_json(msg="Password and keytab cannot be used together")
@@ -199,12 +191,10 @@ def main():
if password is None and admin_keytab is None:
module.fail_json(msg="Password or admin_keytab is needed")
client_domain = hostname[hostname.find(".") + 1:]
nolog = tuple()
env = {'PATH': SECURE_PATH}
fstore = sysrestore.FileStore(paths.IPA_CLIENT_SYSRESTORE)
host_principal = 'host/%s@%s' % (hostname, realm)
sssd = True
options.ca_cert_file = ca_cert_file
options.principal = principal
@@ -215,19 +205,6 @@ def main():
changed = False
already_joined = False
try:
(krb_fd, krb_name) = tempfile.mkstemp()
os.close(krb_fd)
configure_krb5_conf(
cli_realm=realm,
cli_domain=domain,
cli_server=servers,
cli_kdc=kdc,
dnsok=False,
filename=krb_name,
client_domain=client_domain,
client_hostname=hostname,
configure_sssd=sssd,
force=False)
env['KRB5_CONFIG'] = krb_name
ccache_dir = tempfile.mkdtemp(prefix='krbcc')
ccache_name = os.path.join(ccache_dir, 'ccache')
@@ -336,27 +313,17 @@ def main():
paths.IPA_DNS_CCACHE,
config=krb_name,
attempts=kinit_attempts)
env['KRB5CCNAME'] = os.environ['KRB5CCNAME'] = paths.IPA_DNS_CCACHE
except GSSError as e:
# failure to get ticket makes it impossible to login and
# bind from sssd to LDAP, abort installation
module.fail_json(msg="Failed to obtain host TGT: %s" % e)
finally:
try:
os.remove(krb_name)
except OSError:
module.fail_json(msg="Could not remove %s" % krb_name)
if ccache_dir is not None:
try:
os.rmdir(ccache_dir)
except OSError:
pass
if os.path.exists(krb_name + ".ipabkp"):
try:
os.remove(krb_name + ".ipabkp")
except OSError:
module.fail_json(msg="Could not remove %s.ipabkp" % krb_name)
module.exit_json(changed=changed,
already_joined=already_joined)

View File

@@ -0,0 +1,123 @@
# -*- coding: utf-8 -*-
# Authors:
# Thomas Woerner <twoerner@redhat.com>
#
# Based on ipa-client-install code
#
# Copyright (C) 2017-2022 Red Hat
# see file 'COPYING' for use and warranty information
#
# This program is free software; you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>.
from __future__ import (absolute_import, division, print_function)
__metaclass__ = type
ANSIBLE_METADATA = {
'metadata_version': '1.0',
'supported_by': 'community',
'status': ['preview'],
}
DOCUMENTATION = '''
---
module: ipaclient_setup_certmonger
short_description: Setup certmonger for IPA client
description: Setup certmonger for IPA client
options:
realm:
description: Kerberos realm name of the IPA deployment
type: str
required: yes
hostname:
description: Fully qualified name of this host
type: str
required: yes
subject_base:
description: |
The certificate subject base (default O=<realm-name>).
RDNs are in LDAP order (most specific RDN first).
type: str
required: yes
ca_enabled:
description: Whether the Certificate Authority is enabled or not
type: bool
required: yes
request_cert:
description: Request certificate for the machine
type: bool
required: yes
author:
- Thomas Woerner (@t-woerner)
'''
EXAMPLES = '''
- name: Setup certmonger for IPA client
ipaclient_setup_certmonger:
realm: EXAMPLE.COM
hostname: client1.example.com
subject_base: O=EXAMPLE.COM
ca_enabled: true
request_cert: false
'''
RETURN = '''
'''
from ansible.module_utils.basic import AnsibleModule
from ansible.module_utils.ansible_ipa_client import (
setup_logging, check_imports,
options, sysrestore, paths, ScriptError, configure_certmonger
)
def main():
module = AnsibleModule(
argument_spec=dict(
realm=dict(required=True, type='str'),
hostname=dict(required=True, type='str'),
subject_base=dict(required=True, type='str'),
ca_enabled=dict(required=True, type='bool'),
request_cert=dict(required=True, type='bool'),
),
supports_check_mode=False,
)
module._ansible_debug = True
check_imports(module)
setup_logging()
cli_realm = module.params.get('realm')
hostname = module.params.get('hostname')
subject_base = module.params.get('subject_base')
ca_enabled = module.params.get('ca_enabled')
fstore = sysrestore.FileStore(paths.IPA_CLIENT_SYSRESTORE)
options.request_cert = module.params.get('request_cert')
options.hostname = hostname
try:
configure_certmonger(fstore, subject_base, cli_realm, hostname,
options, ca_enabled)
except ScriptError as e:
module.fail_json(msg=str(e))
module.exit_json(changed=True)
if __name__ == '__main__':
main()

View File

@@ -125,6 +125,10 @@ options:
description: Do not configure SSSD as data source for sudo
type: bool
required: no
subid:
description: Configure SSSD as data source for subid
type: bool
required: no
fixed_primary:
description: Configure sssd to use fixed server as primary IPA server
type: bool
@@ -148,6 +152,10 @@ options:
The dist of nss_ldap or nss-pam-ldapd files if sssd is disabled
required: yes
type: dict
krb_name:
description: The krb5 config file name
type: str
required: yes
author:
- Thomas Woerner (@t-woerner)
'''
@@ -163,6 +171,7 @@ EXAMPLES = '''
subject_base: O=EXAMPLE.COM
principal: admin
ca_enabled: yes
krb_name: /tmp/tmpkrb5.conf
'''
RETURN = '''
@@ -177,7 +186,7 @@ from ansible.module_utils.ansible_ipa_client import (
options, sysrestore, paths, ansible_module_get_parsed_ip_addresses,
api, errors, create_ipa_nssdb, ipautil, ScriptError, CLIENT_INSTALL_ERROR,
get_certs_from_ldap, DN, certstore, x509, logger, certdb,
CalledProcessError, tasks, client_dns, configure_certmonger, services,
CalledProcessError, tasks, client_dns, services,
update_ssh_keys, save_state, configure_ldap_conf, configure_nslcd_conf,
configure_openldap_conf, hardcode_ldap_server, getargspec, NUM_VERSION,
serialization
@@ -208,11 +217,13 @@ def main():
no_ssh=dict(required=False, type='bool'),
no_sshd=dict(required=False, type='bool'),
no_sudo=dict(required=False, type='bool'),
subid=dict(required=False, type='bool'),
fixed_primary=dict(required=False, type='bool'),
permit=dict(required=False, type='bool'),
no_krb5_offline_passwords=dict(required=False, type='bool'),
no_dns_sshfp=dict(required=False, type='bool', default=False),
nosssd_files=dict(required=True, type='dict'),
krb_name=dict(required=True, type='str'),
),
supports_check_mode=False,
)
@@ -251,6 +262,7 @@ def main():
options.conf_sshd = not options.no_sshd
options.no_sudo = module.params.get('no_sudo')
options.conf_sudo = not options.no_sudo
options.subid = module.params.get('subid')
options.primary = module.params.get('fixed_primary')
options.permit = module.params.get('permit')
options.no_krb5_offline_passwords = module.params.get(
@@ -262,6 +274,8 @@ def main():
options.sssd = not options.no_sssd
options.no_ac = False
nosssd_files = module.params.get('nosssd_files')
krb_name = module.params.get('krb_name')
os.environ['KRB5_CONFIG'] = krb_name
# pylint: disable=invalid-name
CCACHE_FILE = paths.IPA_DNS_CCACHE
@@ -350,8 +364,6 @@ def main():
if not options.on_master:
client_dns(cli_server[0], hostname, options)
configure_certmonger(fstore, subject_base, cli_realm, hostname,
options, ca_enabled)
if hasattr(paths, "SSH_CONFIG_DIR"):
ssh_config_dir = paths.SSH_CONFIG_DIR
@@ -430,19 +442,17 @@ def main():
# Modify nsswitch/pam stack
# pylint: disable=deprecated-method
argspec = getargspec(tasks.modify_nsswitch_pam_stack)
the_options = {
"sssd": options.sssd,
"mkhomedir": options.mkhomedir,
"statestore": statestore,
}
if "sudo" in argspec.args:
tasks.modify_nsswitch_pam_stack(
sssd=options.sssd,
mkhomedir=options.mkhomedir,
statestore=statestore,
sudo=options.conf_sudo
)
else:
tasks.modify_nsswitch_pam_stack(
sssd=options.sssd,
mkhomedir=options.mkhomedir,
statestore=statestore
)
the_options["sudo"] = options.conf_sudo
if "subid" in argspec.args:
the_options["subid"] = options.subid
tasks.modify_nsswitch_pam_stack(**the_options)
if hasattr(paths, "AUTHSELECT") and paths.AUTHSELECT is not None:
# authselect is used

View File

@@ -0,0 +1,163 @@
# -*- coding: utf-8 -*-
# Authors:
# Thomas Woerner <twoerner@redhat.com>
#
# Based on ipa-client-install code
#
# Copyright (C) 2017-2022 Red Hat
# see file 'COPYING' for use and warranty information
#
# This program is free software; you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>.
from __future__ import (absolute_import, division, print_function)
__metaclass__ = type
ANSIBLE_METADATA = {
'metadata_version': '1.0',
'supported_by': 'community',
'status': ['preview'],
}
DOCUMENTATION = '''
---
module: ipaclient_temp_krb5
short_description:
Create temporary krb5 configuration.
description:
Create temporary krb5 configuration for deferring the creation of the final
krb5.conf on clients
options:
servers:
description: Fully qualified name of IPA servers to enroll to
type: list
elements: str
required: yes
domain:
description: Primary DNS domain of the IPA deployment
type: str
required: yes
realm:
description: Kerberos realm name of the IPA deployment
type: str
required: yes
hostname:
description: Fully qualified name of this host
type: str
required: yes
kdc:
description: The name or address of the host running the KDC
type: str
required: yes
on_master:
description: Whether the configuration is done on the master or not
type: bool
required: no
default: no
author:
- Thomas Woerner (@t-woerner)
'''
EXAMPLES = '''
# Test IPA with local keytab
- name: Test IPA in force mode with maximum 5 kinit attempts
ipaclient_test_keytab:
servers: ["server1.example.com","server2.example.com"]
domain: example.com
realm: EXAMPLE.COM
kdc: server1.example.com
hostname: client1.example.com
# Test IPA with ipadiscovery return values
- name: Join IPA
ipaclient_test_keytab:
servers: "{{ ipadiscovery.servers }}"
domain: "{{ ipadiscovery.domain }}"
realm: "{{ ipadiscovery.realm }}"
kdc: "{{ ipadiscovery.kdc }}"
hostname: "{{ ipadiscovery.hostname }}"
'''
RETURN = '''
krb_name:
description: The krb5 config file name
returned: always
type: str
'''
import os
import tempfile
from ansible.module_utils.basic import AnsibleModule
from ansible.module_utils.ansible_ipa_client import (
setup_logging, check_imports, configure_krb5_conf
)
def main():
module = AnsibleModule(
argument_spec=dict(
servers=dict(required=True, type='list', elements='str'),
domain=dict(required=True, type='str'),
realm=dict(required=True, type='str'),
hostname=dict(required=True, type='str'),
kdc=dict(required=True, type='str'),
on_master=dict(required=False, type='bool', default=False),
),
supports_check_mode=False,
)
module._ansible_debug = True
check_imports(module)
setup_logging()
servers = module.params.get('servers')
domain = module.params.get('domain')
realm = module.params.get('realm')
hostname = module.params.get('hostname')
kdc = module.params.get('kdc')
client_domain = hostname[hostname.find(".") + 1:]
krb_name = None
# Create temporary krb5 configuration
try:
(krb_fd, krb_name) = tempfile.mkstemp()
os.close(krb_fd)
configure_krb5_conf(
cli_realm=realm,
cli_domain=domain,
cli_server=servers,
cli_kdc=kdc,
dnsok=False,
filename=krb_name,
client_domain=client_domain,
client_hostname=hostname,
configure_sssd=True,
force=False)
except Exception as ex:
if krb_name:
try:
os.remove(krb_name)
except OSError:
module.fail_json(msg="Could not remove %s" % krb_name)
module.fail_json(
msg="Failed to create temporary krb5 configuration: %s" % str(ex))
module.exit_json(changed=False,
krb_name=krb_name)
if __name__ == '__main__':
main()

View File

@@ -159,11 +159,29 @@ def main():
ca_crt_exists = os.path.exists(paths.IPA_CA_CRT)
env = {'PATH': SECURE_PATH, 'KRB5CCNAME': paths.IPA_DNS_CCACHE}
# First try: Validate krb5 keytab with system krb5 configuraiton
# First try: Validate with temporary test krb5.conf that forces
# 1) no DNS lookups and
# 2) to load /etc/krb5.conf:
#
# [libdefaults]
# dns_lookup_realm = false
# dns_lookup_kdc = false
# include /etc/krb5.conf
#
try:
(krb_fd, krb_name) = tempfile.mkstemp()
os.close(krb_fd)
content = "\n".join([
"[libdefaults]",
"dns_lookup_realm = false",
"dns_lookup_kdc = false",
"include /etc/krb5.conf"
])
with open(krb_name, "w") as outf:
outf.write(content)
kinit_keytab(host_principal, paths.KRB5_KEYTAB,
paths.IPA_DNS_CCACHE,
config=paths.KRB5_CONF,
config=krb_name,
attempts=kinit_attempts)
krb5_keytab_ok = True
krb5_conf_ok = True
@@ -177,6 +195,11 @@ def main():
pass
except GSSError:
pass
finally:
try:
os.remove(krb_name)
except OSError:
module.fail_json(msg="Could not remove %s" % krb_name)
# Second try: Validate krb5 keytab with temporary krb5
# configuration
@@ -221,6 +244,12 @@ def main():
os.remove(krb_name)
except OSError:
module.fail_json(msg="Could not remove %s" % krb_name)
if os.path.exists(krb_name + ".ipabkp"):
try:
os.remove(krb_name + ".ipabkp")
except OSError:
module.fail_json(
msg="Could not remove %s.ipabkp" % krb_name)
module.exit_json(changed=False,
krb5_keytab_ok=krb5_keytab_ok,

View File

@@ -6,15 +6,15 @@ galaxy_info:
description: A role to join a machine to an IPA domain
company: Red Hat, Inc
license: GPLv3
min_ansible_version: 2.8
min_ansible_version: "2.8"
platforms:
- name: Fedora
versions:
- all
- name: EL
versions:
- 7
- 8
- "7"
- "8"
galaxy_tags:
- identity
- ipa

View File

@@ -2,40 +2,45 @@
# tasks file for ipaclient
- name: Install - Ensure that IPA client packages are installed
package:
ansible.builtin.package:
name: "{{ ipaclient_packages }}"
state: present
when: ipaclient_install_packages | bool
- name: Install - Set ipaclient_servers
set_fact:
ansible.builtin.set_fact:
ipaclient_servers: "{{ groups['ipaservers'] | list }}"
when: groups.ipaservers is defined and ipaclient_servers is not defined
- name: Install - Set ipaclient_servers from cluster inventory
set_fact:
ansible.builtin.set_fact:
ipaclient_servers: "{{ groups['ipaserver'] | list }}"
when: ipaclient_no_dns_lookup | bool and groups.ipaserver is defined and
ipaclient_servers is not defined
- name: Install - Check that either password or keytab is set
fail: msg="ipaadmin_password and ipaadmin_keytab cannot be used together"
ansible.builtin.fail:
msg: "ipaadmin_password and ipaadmin_keytab cannot be used together"
when: ipaadmin_keytab is defined and ipaadmin_password is defined
- name: Install - Set default principal if no keytab is given
set_fact:
ansible.builtin.set_fact:
ipaadmin_principal: admin
when: ipaadmin_principal is undefined and ipaclient_keytab is undefined
- name: Install - Configure DNS resolver Block
- name: Install - DNS resolver configuration
when: ipaclient_configure_dns_resolver | bool
and not ipaclient_on_master | bool
block:
- name: Install - Fail on missing ipaclient_domain and ipaserver_domain
fail: msg="ipaclient_domain or ipaserver_domain is required for ipaclient_configure_dns_resolver"
ansible.builtin.fail:
msg: "ipaclient_domain or ipaserver_domain is required for ipaclient_configure_dns_resolver"
when: ipaserver_domain is not defined and ipaclient_domain is not defined
- name: Install - Fail on missing ipaclient_servers
fail: msg="ipaclient_dns_servers is required for ipaclient_configure_dns_resolver"
ansible.builtin.fail:
msg: "ipaclient_dns_servers is required for ipaclient_configure_dns_resolver"
when: ipaclient_dns_servers is not defined
- name: Install - Configure DNS resolver
@@ -44,9 +49,6 @@
searchdomains: "{{ ipaserver_domain | default(ipaclient_domain) }}"
state: present
when: ipaclient_configure_dns_resolver | bool
and not ipaclient_on_master | bool
- name: Install - IPA client test
ipaclient_test:
### basic ###
@@ -72,9 +74,13 @@
| default(ipasssd_enable_dns_updates) }}"
register: result_ipaclient_test
- block:
- name: Install - Client deployment
when: not ansible_check_mode and
not (result_ipaclient_test.client_already_configured and
not ipaclient_allow_repair | bool and not ipaclient_force_join | bool)
block:
- name: Install - Cleanup leftover ccache
file:
ansible.builtin.file:
path: "/etc/ipa/.dns_ccache"
state: absent
@@ -91,12 +97,12 @@
domain: "{{ result_ipaclient_test.domain }}"
- name: Install - Make sure One-Time Password is enabled if it's already defined
set_fact:
ansible.builtin.set_fact:
ipaclient_use_otp: "yes"
when: ipaclient_otp is defined
- name: Install - Disable One-Time Password for on_master
set_fact:
ansible.builtin.set_fact:
ipaclient_use_otp: "no"
when: ipaclient_use_otp | bool and ipaclient_on_master | bool
@@ -112,7 +118,7 @@
- name: Install - Disable One-Time Password for client with working
krb5.keytab
set_fact:
ansible.builtin.set_fact:
ipaclient_use_otp: "no"
when: ipaclient_use_otp | bool and
result_ipaclient_test_keytab.krb5_keytab_ok and
@@ -125,10 +131,12 @@
# to create a OneTime Password
# If a keytab is specified in the hostent, then the hostent will be disabled
# if ipaclient_use_otp is set.
- block:
- name: Install - Obtain OTP
when: ipaclient_use_otp | bool and ipaclient_otp is not defined
block:
- name: Install - Keytab or password is required for getting otp
ansible.builtin.fail:
msg: Keytab or password is required for getting otp
msg: "Keytab or password is required for getting otp"
when: ipaadmin_keytab is undefined and ipaadmin_password is undefined
- name: Install - Create temporary file for keytab
@@ -159,20 +167,17 @@
delegate_to: "{{ result_ipaclient_test.servers[0] }}"
- name: Install - Report error for OTP generation
debug:
ansible.builtin.debug:
msg: "{{ result_ipaclient_get_otp.msg }}"
when: result_ipaclient_get_otp is failed
failed_when: yes
- name: Install - Store the previously obtained OTP
no_log: yes
set_fact:
ansible.builtin.set_fact:
ipaadmin_orig_password: "{{ ipaadmin_password | default(omit) }}"
ipaadmin_password: "{{ result_ipaclient_get_otp.host.randompassword
if result_ipaclient_get_otp.host is defined }}"
when: ipaclient_use_otp | bool and ipaclient_otp is not defined
always:
- name: Install - Remove keytab temporary file
ansible.builtin.file:
@@ -183,12 +188,14 @@
- name: Store predefined OTP in admin_password
no_log: yes
set_fact:
ansible.builtin.set_fact:
ipaadmin_orig_password: "{{ ipaadmin_password | default(omit) }}"
ipaadmin_password: "{{ ipaclient_otp }}"
when: ipaclient_otp is defined
- block:
- name: Install - Check keytab, principal and keytab
when: not ipaclient_on_master | bool
block:
# This block is executed only when
# not (not ipaclient_on_master | bool and
# not result_ipaclient_join.changed and
@@ -198,19 +205,20 @@
# result_ipaclient_join.already_joined)))
- name: Install - Check if principal and keytab are set
fail: msg="Admin principal and client keytab cannot be used together"
ansible.builtin.fail:
msg: "Admin principal and client keytab cannot be used together"
when: ipaadmin_principal is defined and ipaclient_keytab is defined
- name: Install - Check if one of password or keytabs are set
fail: msg="At least one of password or keytabs must be specified"
ansible.builtin.fail:
msg: "At least one of password or keytabs must be specified"
when: not result_ipaclient_test_keytab.krb5_keytab_ok
and ipaadmin_password is undefined
and ipaadmin_keytab is undefined
and ipaclient_keytab is undefined
when: not ipaclient_on_master | bool
- name: Install - Purge {{ result_ipaclient_test.realm }} from host keytab
command: >
- name: "Install - From host keytab, purge {{ result_ipaclient_test.realm }}"
ansible.builtin.command: >
/usr/sbin/ipa-rmkeytab
-k /etc/krb5.keytab
-r "{{ result_ipaclient_test.realm }}"
@@ -231,12 +239,19 @@
hostname: "{{ result_ipaclient_test.hostname }}"
when: not ipaclient_on_master | bool
- name: Install - Join IPA
ipaclient_join:
- name: Install - Create temporary krb5 configuration
ipaclient_temp_krb5:
servers: "{{ result_ipaclient_test.servers }}"
domain: "{{ result_ipaclient_test.domain }}"
realm: "{{ result_ipaclient_test.realm }}"
hostname: "{{ result_ipaclient_test.hostname }}"
kdc: "{{ result_ipaclient_test.kdc }}"
register: result_ipaclient_temp_krb5
- name: Install - Join IPA
ipaclient_join:
servers: "{{ result_ipaclient_test.servers }}"
realm: "{{ result_ipaclient_test.realm }}"
basedn: "{{ result_ipaclient_test.basedn }}"
hostname: "{{ result_ipaclient_test.hostname }}"
force_join: "{{ ipaclient_force_join | default(omit) }}"
@@ -247,35 +262,44 @@
admin_keytab: "{{ ipaadmin_keytab if ipaadmin_keytab is defined and not ipaclient_use_otp | bool else omit }}"
# ca_cert_file: "{{ ipaclient_ca_cert_file | default(omit) }}"
kinit_attempts: "{{ ipaclient_kinit_attempts | default(omit) }}"
krb_name: "{{ result_ipaclient_temp_krb5.krb_name }}"
register: result_ipaclient_join
when: not ipaclient_on_master | bool and
(not result_ipaclient_test_keytab.krb5_keytab_ok or
ipaclient_force_join)
- block:
- name: krb5 configuration not correct
fail:
msg: >
The krb5 configuration is not correct, please enable allow_repair
to fix this.
when: not result_ipaclient_test_keytab.krb5_conf_ok
- name: IPA test failed
fail:
msg: "The IPA test failed, please enable allow_repair to fix this."
when: not result_ipaclient_test_keytab.ping_test_ok
- name: ca.crt file is missing
fail:
msg: >
The ca.crt file is missing, please enable allow_repair to fix this.
when: not result_ipaclient_test_keytab.ca_crt_exists
- name: Install - Allow repair checks
when: not ipaclient_on_master | bool and
not result_ipaclient_join.changed and
not ipaclient_allow_repair | bool and
(result_ipaclient_test_keytab.krb5_keytab_ok or
(result_ipaclient_join.already_joined is defined and
result_ipaclient_join.already_joined))
block:
- name: The krb5 configuration is not correct
ansible.builtin.fail:
msg: >
The krb5 configuration is not correct, please enable allow_repair
to fix this.
when: not result_ipaclient_test_keytab.krb5_conf_ok
- name: IPA test failed
ansible.builtin.fail:
msg: "The IPA test failed, please enable allow_repair to fix this."
when: not result_ipaclient_test_keytab.ping_test_ok
- name: Fail due to missing ca.crt file
ansible.builtin.fail:
msg: >
The ca.crt file is missing, please enable allow_repair to fix this.
when: not result_ipaclient_test_keytab.ca_crt_exists
- block:
- name: Install - Configuration
when: not (not ipaclient_on_master | bool and
not result_ipaclient_join.changed and
not ipaclient_allow_repair | bool
and (result_ipaclient_test_keytab.krb5_keytab_ok
or (result_ipaclient_join.already_joined is defined
and result_ipaclient_join.already_joined)))
block:
- name: Install - Configure IPA default.conf
ipaclient_ipa_conf:
servers: "{{ result_ipaclient_test.servers }}"
@@ -307,26 +331,13 @@
"{{ ipassd_no_krb5_offline_passwords
| default(ipasssd_no_krb5_offline_passwords) }}"
- name: Install - Configure krb5 for IPA realm
ipaclient_setup_krb5:
realm: "{{ result_ipaclient_test.realm }}"
domain: "{{ result_ipaclient_test.domain }}"
servers: "{{ result_ipaclient_test.servers }}"
kdc: "{{ result_ipaclient_test.kdc }}"
dnsok: "{{ result_ipaclient_test.dnsok }}"
client_domain: "{{ result_ipaclient_test.client_domain }}"
hostname: "{{ result_ipaclient_test.hostname }}"
sssd: "{{ result_ipaclient_test.sssd }}"
force: "{{ ipaclient_force }}"
# on_master: "{{ ipaclient_on_master }}"
when: not ipaclient_on_master | bool
- name: Install - IPA API calls for remaining enrollment parts
ipaclient_api:
servers: "{{ result_ipaclient_test.servers }}"
realm: "{{ result_ipaclient_test.realm }}"
hostname: "{{ result_ipaclient_test.hostname }}"
# debug: yes
krb_name: "{{ result_ipaclient_temp_krb5.krb_name }}"
register: result_ipaclient_api
- name: Install - Fix IPA ca
@@ -335,6 +346,7 @@
realm: "{{ result_ipaclient_test.realm }}"
basedn: "{{ result_ipaclient_test.basedn }}"
allow_repair: "{{ ipaclient_allow_repair }}"
krb_name: "{{ result_ipaclient_temp_krb5.krb_name }}"
when: not ipaclient_on_master | bool and
result_ipaclient_test_keytab.krb5_keytab_ok and
not result_ipaclient_test_keytab.ca_crt_exists
@@ -362,6 +374,7 @@
no_ssh: "{{ ipaclient_no_ssh }}"
no_sshd: "{{ ipaclient_no_sshd }}"
no_sudo: "{{ ipaclient_no_sudo }}"
subid: "{{ ipaclient_subid }}"
fixed_primary: "{{ ipassd_fixed_primary
| default(ipasssd_fixed_primary) }}"
permit: "{{ ipassd_permit | default(ipasssd_permit) }}"
@@ -370,6 +383,7 @@
| default(ipasssd_no_krb5_offline_passwords) }}"
no_dns_sshfp: "{{ ipaclient_no_dns_sshfp }}"
nosssd_files: "{{ result_ipaclient_test.nosssd_files }}"
krb_name: "{{ result_ipaclient_temp_krb5.krb_name }}"
- name: Install - Configure SSH and SSHD
ipaclient_setup_ssh:
@@ -397,25 +411,55 @@
nisdomain: "{{ ipaclient_nisdomain | default(omit) }}"
when: not ipaclient_no_nisdomain | bool
when: not (not ipaclient_on_master | bool and
not result_ipaclient_join.changed and
not ipaclient_allow_repair | bool
and (result_ipaclient_test_keytab.krb5_keytab_ok
or (result_ipaclient_join.already_joined is defined
and result_ipaclient_join.already_joined)))
- name: Remove temporary krb5.conf
ansible.builtin.file:
path: "{{ result_ipaclient_temp_krb5.krb_name }}"
state: absent
when: result_ipaclient_temp_krb5.krb_name is defined
when: not ansible_check_mode and
not (result_ipaclient_test.client_already_configured and
not ipaclient_allow_repair | bool and not ipaclient_force_join | bool)
- name: Install - Configure krb5 for IPA realm
ipaclient_setup_krb5:
realm: "{{ result_ipaclient_test.realm }}"
domain: "{{ result_ipaclient_test.domain }}"
servers: "{{ result_ipaclient_test.servers }}"
kdc: "{{ result_ipaclient_test.kdc }}"
dnsok: "{{ result_ipaclient_test.dnsok }}"
client_domain: "{{ result_ipaclient_test.client_domain }}"
hostname: "{{ result_ipaclient_test.hostname }}"
sssd: "{{ result_ipaclient_test.sssd }}"
force: "{{ ipaclient_force }}"
# on_master: "{{ ipaclient_on_master }}"
when: not ipaclient_on_master | bool
- name: Install - Configure certmonger
ipaclient_setup_certmonger:
realm: "{{ result_ipaclient_test.realm }}"
hostname: "{{ result_ipaclient_test.hostname }}"
subject_base: "{{ result_ipaclient_api.subject_base }}"
ca_enabled: "{{ result_ipaclient_api.ca_enabled }}"
request_cert: "{{ ipaclient_request_cert }}"
when: not ipaclient_on_master | bool
always:
- name: Install - Restore original admin password if overwritten by OTP
no_log: yes
set_fact:
ansible.builtin.set_fact:
ipaadmin_password: "{{ ipaadmin_orig_password }}"
when: ipaclient_use_otp | bool and ipaadmin_orig_password is defined
- name: Cleanup leftover ccache
file:
ansible.builtin.file:
path: "/etc/ipa/.dns_ccache"
state: absent
- name: Remove temporary krb5.conf
ansible.builtin.file:
path: "{{ result_ipaclient_temp_krb5.krb_name }}"
state: absent
when: result_ipaclient_temp_krb5.krb_name is defined
- name: Remove temporary krb5.conf backup
ansible.builtin.file:
path: "{{ result_ipaclient_temp_krb5.krb_name }}.ipabkp"
state: absent
when: result_ipaclient_temp_krb5.krb_name is defined

View File

@@ -2,7 +2,7 @@
# tasks file for ipaclient
- name: Import variables specific to distribution
include_vars: "{{ item }}"
ansible.builtin.include_vars: "{{ item }}"
with_first_found:
- "{{ role_path }}/vars/{{ ansible_facts['distribution'] }}-{{ ansible_facts['distribution_version'] }}.yml"
- "{{ role_path }}/vars/{{ ansible_facts['distribution'] }}-{{ ansible_facts['distribution_major_version'] }}.yml"
@@ -17,9 +17,9 @@
- "{{ role_path }}/vars/default.yml"
- name: Install IPA client
include_tasks: install.yml
ansible.builtin.include_tasks: install.yml
when: state|default('present') == 'present'
- name: Uninstall IPA client
include_tasks: uninstall.yml
ansible.builtin.include_tasks: uninstall.yml
when: state|default('present') == 'absent'

View File

@@ -2,7 +2,7 @@
# tasks to uninstall IPA client
- name: Uninstall - Uninstall IPA client
command: >
ansible.builtin.command: >
/usr/sbin/ipa-client-install
--uninstall
-U
@@ -17,6 +17,6 @@
when: ipaclient_cleanup_dns_resolver | bool
#- name: Remove IPA client package
# package:
# ansible.builtin.package:
# name: "{{ ipaclient_packages }}"
# state: absent

View File

@@ -114,6 +114,50 @@ Example playbook to setup the IPA client(s) using principal and password from in
state: present
```
Example inventory file to remove a replica from the domain:
```ini
[ipareplicas]
ipareplica1.example.com
[ipareplicas:vars]
ipaadmin_password=MySecretPassword123
ipareplica_remove_from_domain=true
```
Example playbook to remove an IPA replica using admin passwords from the domain:
```yaml
---
- name: Playbook to remove IPA replica
hosts: ipareplica
become: true
roles:
- role: ipareplica
state: absent
```
The inventory will enable the removal of the replica (also a replica) from the domain. Additional options are needed if the removal of the replica is resulting in a topology disconnect or if the replica is the last that has a role.
To continue with the removal with a topology disconnect it is needed to set these parameters:
```ini
ipareplica_ignore_topology_disconnect=true
ipareplica_remove_on_server=ipareplica2.example.com
```
To continue with the removal for a replica that is the last that has a role:
```ini
ipareplica_ignore_last_of_role=true
```
Be careful with enabling the `ipareplica_ignore_topology_disconnect` and especially `ipareplica_ignore_last_of_role`, the change can not be reverted easily.
The parameters `ipaserver_ignore_topology_disconnect`, `ipaserver_ignore_last_of_role`, `ipaserver_remove_on_server` and `ipaserver_remove_from_domain` can be used instead.
Playbooks
=========
@@ -200,6 +244,7 @@ Variable | Description | Required
`ipaclient_no_ssh` | The bool value defines if OpenSSH client will be configured. (bool, default: false) | no
`ipaclient_no_sshd` | The bool value defines if OpenSSH server will be configured. (bool, default: false) | no
`ipaclient_no_sudo` | The bool value defines if SSSD will be configured as a data source for sudo. (bool, default: false) | no
`ipaclient_subid` | The bool value defines if SSSD will be configured as a data source for subid. (bool, default: false) | no
`ipaclient_no_dns_sshfp` | The bool value defines if DNS SSHFP records will not be created automatically. (bool, default: false) | no
Certificate system Variables
@@ -254,6 +299,19 @@ Variable | Description | Required
`ipareplica_setup_firewalld` | The value defines if the needed services will automatically be openen in the firewall managed by firewalld. (bool, default: true) | no
`ipareplica_firewalld_zone` | The value defines the firewall zone that will be used. This needs to be an existing runtime and permanent zone. (string) | no
Undeploy Variables (`state`: absent)
------------------------------------
These settings should only be used if the result is really wanted. The change might not be revertable easily.
Variable | Description | Required
-------- | ----------- | --------
`ipareplica_ignore_topology_disconnect` \| `ipaserver_ignore_topology_disconnect` | If enabled this enforces the removal of the replica even if it results in a topology disconnect. Be careful with this setting. (bool) | false
`ipareplica_ignore_last_of_role` \| `ipaserver_ignore_last_of_role` | If enabled this enforces the removal of the replica even if the replica is the last with one that has a role. Be careful, this might not be revered easily. (bool) | false
`ipareplica_remove_from_domain` \| `ipaserver_remove_from_domain` | This enables the removal of the replica from the domain additionally to the undeployment. (bool) | false
`ipareplica_remove_on_server` \| `ipaserver_remove_on_server` | The value defines the replica in the domain that will to be used to remove the replica from the domain if `ipareplica_ignore_topology_disconnect` and `ipareplica_remove_from_domain` are enabled. Without the need to enable `ipareplica_ignore_topology_disconnect`, the value will be automatically detected using the replication agreements of the replica. (string) | false
Authors
=======

View File

@@ -6,15 +6,15 @@ galaxy_info:
description: A role to setup an IPA domain replica
company: Red Hat, Inc
license: GPLv3
min_ansible_version: 2.8
min_ansible_version: "2.8"
platforms:
- name: Fedora
versions:
- all
- name: EL
versions:
- 7
- 8
- "7"
- "8"
galaxy_tags:
- identity
- ipa

View File

@@ -1,64 +1,64 @@
---
# tasks file for ipareplica
- block:
- name: Package installation
when: ipareplica_install_packages | bool
block:
- name: Install - Ensure IPA replica packages are installed
package:
ansible.builtin.package:
name: "{{ ipareplica_packages }}"
state: present
- name: Install - Ensure IPA replica packages for dns are installed
package:
ansible.builtin.package:
name: "{{ ipareplica_packages_dns }}"
state: present
when: ipareplica_setup_dns | bool
- name: Install - Ensure IPA replica packages for adtrust are installed
package:
ansible.builtin.package:
name: "{{ ipareplica_packages_adtrust }}"
state: present
when: ipareplica_setup_adtrust | bool
- name: Install - Ensure that firewall packages installed
package:
ansible.builtin.package:
name: "{{ ipareplica_packages_firewalld }}"
state: present
when: ipareplica_setup_firewalld | bool
when: ipareplica_install_packages | bool
- block:
- name: Firewall configuration
when: ipareplica_setup_firewalld | bool
block:
- name: Firewalld service - Ensure that firewalld is running
systemd:
ansible.builtin.systemd:
name: firewalld
enabled: yes
state: started
- name: Firewalld - Verify runtime zone "{{ ipareplica_firewalld_zone }}"
shell: >
ansible.builtin.shell: >
firewall-cmd
--info-zone="{{ ipareplica_firewalld_zone }}"
>/dev/null
when: ipareplica_firewalld_zone is defined
- name: Firewalld - Verify permanent zone "{{ ipareplica_firewalld_zone }}"
shell: >
ansible.builtin.shell: >
firewall-cmd
--permanent
--info-zone="{{ ipareplica_firewalld_zone }}"
>/dev/null
when: ipareplica_firewalld_zone is defined
when: ipareplica_setup_firewalld | bool
- name: Install - Set ipareplica_servers
set_fact:
ansible.builtin.set_fact:
ipareplica_servers: "{{ groups['ipaservers'] | list }}"
when: groups.ipaservers is defined and ipareplica_servers is not defined
- name: Install - Set default principal if no keytab is given
set_fact:
ansible.builtin.set_fact:
ipaadmin_principal: admin
when: ipaadmin_principal is undefined and ipaclient_keytab is undefined
@@ -71,7 +71,7 @@
domain: "{{ ipareplica_domain | default(ipaserver_domain) |
default(omit) }}"
servers: "{{ ipareplica_servers | default(omit) }}"
realm: "{{ ipareplica_realm | default(ipaserver_realm) |default(omit) }}"
realm: "{{ ipareplica_realm | default(ipaserver_realm) | default(omit) }}"
hostname: "{{ ipareplica_hostname | default(ansible_facts['fqdn']) }}"
ca_cert_files: "{{ ipareplica_ca_cert_files | default([]) }}"
hidden_replica: "{{ ipareplica_hidden_replica }}"
@@ -101,14 +101,18 @@
no_dnssec_validation: "{{ ipareplica_no_dnssec_validation }}"
register: result_ipareplica_test
- block:
- name: Install - Deploy replica
when: not ansible_check_mode and
not (result_ipareplica_test.client_already_configured is defined or
result_ipareplica_test.server_already_configured is defined)
block:
# This block is executed only when
# not ansible_check_mode and
# not (result_ipareplica_test.client_already_configured is defined or
# result_ipareplica_test.server_already_configured is defined)
- name: Install - Setup client
include_role:
ansible.builtin.include_role:
name: ipaclient
vars:
state: present
@@ -120,7 +124,7 @@
when: not result_ipareplica_test.client_enrolled
- name: Install - Configure firewalld
command: >
ansible.builtin.command: >
firewall-cmd
--permanent
--zone="{{ ipareplica_firewalld_zone if ipareplica_firewalld_zone is
@@ -134,7 +138,7 @@
when: ipareplica_setup_firewalld | bool
- name: Install - Configure firewalld runtime
command: >
ansible.builtin.command: >
firewall-cmd
--zone="{{ ipareplica_firewalld_zone if ipareplica_firewalld_zone is
defined else '' }}"
@@ -222,8 +226,8 @@
- name: Install - Set dirman password
no_log: yes
set_fact:
ipareplica_dirman_password:
ansible.builtin.set_fact:
__derived_dirman_password:
"{{ result_ipareplica_master_password.password }}"
- name: Install - Setup certmonger
@@ -251,10 +255,6 @@
dirsrv_cert_files: "{{ ipareplica_dirsrv_cert_files | default([]) }}"
### client ###
force_join: "{{ ipaclient_force_join }}"
### ad trust ###
netbios_name: "{{ ipareplica_netbios_name | default(omit) }}"
rid_base: "{{ ipareplica_rid_base | default(omit) }}"
secondary_rid_base: "{{ ipareplica_secondary_rid_base | default(omit) }}"
### additional ###
server: "{{ result_ipareplica_test.server }}"
ccache: "{{ result_ipareplica_prepare.ccache }}"
@@ -264,7 +264,7 @@
_add_to_ipaservers: "{{ result_ipareplica_prepare._add_to_ipaservers }}"
_ca_subject: "{{ result_ipareplica_prepare._ca_subject }}"
_subject_base: "{{ result_ipareplica_prepare._subject_base }}"
dirman_password: "{{ ipareplica_dirman_password }}"
dirman_password: "{{ __derived_dirman_password }}"
config_setup_ca: "{{ result_ipareplica_prepare.config_setup_ca }}"
config_master_host_name:
"{{ result_ipareplica_prepare.config_master_host_name }}"
@@ -293,22 +293,18 @@
dirsrv_cert_files: "{{ ipareplica_dirsrv_cert_files | default([]) }}"
### client ###
force_join: "{{ ipaclient_force_join }}"
### ad trust ###
netbios_name: "{{ ipareplica_netbios_name | default(omit) }}"
rid_base: "{{ ipareplica_rid_base | default(omit) }}"
secondary_rid_base: "{{ ipareplica_secondary_rid_base | default(omit) }}"
### additional ###
server: "{{ result_ipareplica_test.server }}"
ccache: "{{ result_ipareplica_prepare.ccache }}"
installer_ccache: "{{ result_ipareplica_prepare.installer_ccache }}"
_ca_enabled: "{{ result_ipareplica_prepare._ca_enabled }}"
_dirsrv_pkcs12_info: "{{ result_ipareplica_prepare._dirsrv_pkcs12_info if result_ipareplica_prepare._dirsrv_pkcs12_info != None else omit }}"
_dirsrv_pkcs12_info: "{{ result_ipareplica_prepare._dirsrv_pkcs12_info if result_ipareplica_prepare._dirsrv_pkcs12_info != None else omit }}"
subject_base: "{{ result_ipareplica_prepare.subject_base }}"
_top_dir: "{{ result_ipareplica_prepare._top_dir }}"
_add_to_ipaservers: "{{ result_ipareplica_prepare._add_to_ipaservers }}"
_ca_subject: "{{ result_ipareplica_prepare._ca_subject }}"
_subject_base: "{{ result_ipareplica_prepare._subject_base }}"
dirman_password: "{{ ipareplica_dirman_password }}"
dirman_password: "{{ __derived_dirman_password }}"
config_setup_ca: "{{ result_ipareplica_prepare.config_setup_ca }}"
config_master_host_name:
"{{ result_ipareplica_install_ca_certs.config_master_host_name }}"
@@ -335,10 +331,6 @@
dirsrv_cert_files: "{{ ipareplica_dirsrv_cert_files | default([]) }}"
### client ###
force_join: "{{ ipaclient_force_join }}"
### ad trust ###
netbios_name: "{{ ipareplica_netbios_name | default(omit) }}"
rid_base: "{{ ipareplica_rid_base | default(omit) }}"
secondary_rid_base: "{{ ipareplica_secondary_rid_base | default(omit) }}"
### additional ###
server: "{{ result_ipareplica_test.server }}"
config_master_host_name:
@@ -352,7 +344,7 @@
_add_to_ipaservers: "{{ result_ipareplica_prepare._add_to_ipaservers }}"
_ca_subject: "{{ result_ipareplica_prepare._ca_subject }}"
_subject_base: "{{ result_ipareplica_prepare._subject_base }}"
dirman_password: "{{ ipareplica_dirman_password }}"
dirman_password: "{{ __derived_dirman_password }}"
setup_ca: "{{ result_ipareplica_prepare.config_setup_ca }}"
- name: Install - Setup KRB
@@ -367,9 +359,9 @@
config_master_host_name:
"{{ result_ipareplica_install_ca_certs.config_master_host_name }}"
ccache: "{{ result_ipareplica_prepare.ccache }}"
_pkinit_pkcs12_info: "{{ result_ipareplica_prepare._pkinit_pkcs12_info if result_ipareplica_prepare._pkinit_pkcs12_info != None else omit }}"
_pkinit_pkcs12_info: "{{ result_ipareplica_prepare._pkinit_pkcs12_info if result_ipareplica_prepare._pkinit_pkcs12_info != None else omit }}"
_top_dir: "{{ result_ipareplica_prepare._top_dir }}"
dirman_password: "{{ ipareplica_dirman_password }}"
dirman_password: "{{ __derived_dirman_password }}"
# We need to point to the master in ipa default conf when certmonger
# asks for HTTP certificate in newer ipa versions. In these versions
@@ -393,10 +385,6 @@
dirsrv_cert_files: "{{ ipareplica_dirsrv_cert_files | default([]) }}"
### client ###
force_join: "{{ ipaclient_force_join }}"
### ad trust ###
netbios_name: "{{ ipareplica_netbios_name | default(omit) }}"
rid_base: "{{ ipareplica_rid_base | default(omit) }}"
secondary_rid_base: "{{ ipareplica_secondary_rid_base | default(omit) }}"
### additional ###
server: "{{ result_ipareplica_test.server }}"
config_master_host_name:
@@ -410,7 +398,7 @@
_add_to_ipaservers: "{{ result_ipareplica_prepare._add_to_ipaservers }}"
_ca_subject: "{{ result_ipareplica_prepare._ca_subject }}"
_subject_base: "{{ result_ipareplica_prepare._subject_base }}"
dirman_password: "{{ ipareplica_dirman_password }}"
dirman_password: "{{ __derived_dirman_password }}"
setup_ca: "{{ result_ipareplica_prepare.config_setup_ca }}"
master:
"{{ result_ipareplica_install_ca_certs.config_master_host_name }}"
@@ -434,7 +422,7 @@
_dirsrv_pkcs12_info: "{{ result_ipareplica_prepare._dirsrv_pkcs12_info if result_ipareplica_prepare._dirsrv_pkcs12_info != None else omit }}"
_pkinit_pkcs12_info: "{{ result_ipareplica_prepare._pkinit_pkcs12_info if result_ipareplica_prepare._pkinit_pkcs12_info != None else omit }}"
_top_dir: "{{ result_ipareplica_prepare._top_dir }}"
dirman_password: "{{ ipareplica_dirman_password }}"
dirman_password: "{{ __derived_dirman_password }}"
ds_ca_subject: "{{ result_ipareplica_setup_ds.ds_ca_subject }}"
- name: Install - Setup http
@@ -455,7 +443,7 @@
_ca_file: "{{ result_ipareplica_prepare._ca_file }}"
_http_pkcs12_info: "{{ result_ipareplica_prepare._http_pkcs12_info if result_ipareplica_prepare._http_pkcs12_info != None else omit }}"
_top_dir: "{{ result_ipareplica_prepare._top_dir }}"
dirman_password: "{{ ipareplica_dirman_password }}"
dirman_password: "{{ __derived_dirman_password }}"
# Need to point back to ourself after the cert for HTTP is obtained
- name: Install - Create original IPA conf again
@@ -477,10 +465,6 @@
dirsrv_cert_files: "{{ ipareplica_dirsrv_cert_files | default([]) }}"
### client ###
force_join: "{{ ipaclient_force_join }}"
### ad trust ###
netbios_name: "{{ ipareplica_netbios_name | default(omit) }}"
rid_base: "{{ ipareplica_rid_base | default(omit) }}"
secondary_rid_base: "{{ ipareplica_secondary_rid_base | default(omit) }}"
### additional ###
server: "{{ result_ipareplica_test.server }}"
config_master_host_name:
@@ -494,7 +478,7 @@
_add_to_ipaservers: "{{ result_ipareplica_prepare._add_to_ipaservers }}"
_ca_subject: "{{ result_ipareplica_prepare._ca_subject }}"
_subject_base: "{{ result_ipareplica_prepare._subject_base }}"
dirman_password: "{{ ipareplica_dirman_password }}"
dirman_password: "{{ __derived_dirman_password }}"
setup_ca: "{{ result_ipareplica_prepare.config_setup_ca }}"
when: result_ipareplica_test.change_master_for_certmonger
@@ -513,7 +497,7 @@
ccache: "{{ result_ipareplica_prepare.ccache }}"
_ca_file: "{{ result_ipareplica_prepare._ca_file }}"
_top_dir: "{{ result_ipareplica_prepare._top_dir }}"
dirman_password: "{{ ipareplica_dirman_password }}"
dirman_password: "{{ __derived_dirman_password }}"
- name: Install - Setup custodia
ipareplica_setup_custodia:
@@ -534,7 +518,7 @@
_ca_file: "{{ result_ipareplica_prepare._ca_file }}"
_pkinit_pkcs12_info: "{{ result_ipareplica_prepare._pkinit_pkcs12_info if result_ipareplica_prepare._pkinit_pkcs12_info != None else omit }}"
_top_dir: "{{ result_ipareplica_prepare._top_dir }}"
dirman_password: "{{ ipareplica_dirman_password }}"
dirman_password: "{{ __derived_dirman_password }}"
- name: Install - Setup CA
ipareplica_setup_ca:
@@ -557,7 +541,7 @@
_pkinit_pkcs12_info: "{{ result_ipareplica_prepare._pkinit_pkcs12_info if result_ipareplica_prepare._pkinit_pkcs12_info != None else omit }}"
_top_dir: "{{ result_ipareplica_prepare._top_dir }}"
_random_serial_numbers: "{{ result_ipareplica_prepare._random_serial_numbers }}"
dirman_password: "{{ ipareplica_dirman_password }}"
dirman_password: "{{ __derived_dirman_password }}"
config_setup_ca: "{{ result_ipareplica_prepare.config_setup_ca }}"
config_master_host_name:
"{{ result_ipareplica_install_ca_certs.config_master_host_name }}"
@@ -582,7 +566,7 @@
_ca_file: "{{ result_ipareplica_prepare._ca_file }}"
_pkinit_pkcs12_info: "{{ result_ipareplica_prepare._pkinit_pkcs12_info if result_ipareplica_prepare._pkinit_pkcs12_info != None else omit }}"
_top_dir: "{{ result_ipareplica_prepare._top_dir }}"
dirman_password: "{{ ipareplica_dirman_password }}"
dirman_password: "{{ __derived_dirman_password }}"
- name: Install - DS apply updates
ipareplica_ds_apply_updates:
@@ -602,7 +586,7 @@
_ca_file: "{{ result_ipareplica_prepare._ca_file }}"
_pkinit_pkcs12_info: "{{ result_ipareplica_prepare._pkinit_pkcs12_info if result_ipareplica_prepare._pkinit_pkcs12_info != None else omit }}"
_top_dir: "{{ result_ipareplica_prepare._top_dir }}"
dirman_password: "{{ ipareplica_dirman_password }}"
dirman_password: "{{ __derived_dirman_password }}"
ds_ca_subject: "{{ result_ipareplica_setup_ds.ds_ca_subject }}"
- name: Install - Setup kra
@@ -642,7 +626,7 @@
_add_to_ipaservers: "{{ result_ipareplica_prepare._add_to_ipaservers }}"
_ca_subject: "{{ result_ipareplica_prepare._ca_subject }}"
_subject_base: "{{ result_ipareplica_prepare._subject_base }}"
dirman_password: "{{ ipareplica_dirman_password }}"
dirman_password: "{{ __derived_dirman_password }}"
when: result_ipareplica_test.setup_kra
- name: Install - Restart KDC
@@ -660,7 +644,7 @@
ccache: "{{ result_ipareplica_prepare.ccache }}"
_ca_file: "{{ result_ipareplica_prepare._ca_file }}"
_top_dir: "{{ result_ipareplica_prepare._top_dir }}"
dirman_password: "{{ ipareplica_dirman_password }}"
dirman_password: "{{ __derived_dirman_password }}"
- name: Install - Custodia import dm password
ipareplica_custodia_import_dm_password:
@@ -681,7 +665,7 @@
_kra_enabled: "{{ result_ipareplica_prepare._kra_enabled }}"
_kra_host_name: "{{ result_ipareplica_prepare.config_kra_host_name }}"
_top_dir: "{{ result_ipareplica_prepare._top_dir }}"
dirman_password: "{{ ipareplica_dirman_password }}"
dirman_password: "{{ __derived_dirman_password }}"
config_setup_ca: "{{ result_ipareplica_prepare.config_setup_ca }}"
- name: Install - Promote SSSD
@@ -775,22 +759,17 @@
"{{ result_ipareplica_prepare.config_master_host_name }}"
register: result_ipareplica_enable_ipa
always:
- name: Install - Cleanup root IPA cache
file:
ansible.builtin.file:
path: "/root/.ipa_cache"
state: absent
when: result_ipareplica_enable_ipa.changed
always:
- name: Cleanup temporary files
file:
ansible.builtin.file:
path: "{{ item }}"
state: absent
with_items:
- "/etc/ipa/.tmp_pkcs12_dirsrv"
- "/etc/ipa/.tmp_pkcs12_http"
- "/etc/ipa/.tmp_pkcs12_pkinit"
when: not ansible_check_mode and
not (result_ipareplica_test.client_already_configured is defined or
result_ipareplica_test.server_already_configured is defined)

View File

@@ -2,7 +2,7 @@
# tasks file for ipareplica
- name: Import variables specific to distribution
include_vars: "{{ item }}"
ansible.builtin.include_vars: "{{ item }}"
with_first_found:
- "vars/{{ ansible_facts['distribution'] }}-{{ ansible_facts['distribution_version'] }}.yml"
- "vars/{{ ansible_facts['distribution'] }}-{{ ansible_facts['distribution_major_version'] }}.yml"
@@ -17,9 +17,9 @@
- "vars/default.yml"
- name: Install IPA replica
include_tasks: install.yml
ansible.builtin.include_tasks: install.yml
when: state|default('present') == 'present'
- name: Uninstall IPA replica
include_tasks: uninstall.yml
ansible.builtin.include_tasks: uninstall.yml
when: state|default('present') == 'absent'

View File

@@ -1,37 +1,19 @@
---
# tasks to uninstall IPA replica
- name: Uninstall - Uninstall IPA replica
command: >
/usr/sbin/ipa-server-install
--uninstall
-U
{{ "--ignore-topology-disconnect" if
ipareplica_ignore_topology_disconnect | bool else "" }}
{{ "--ignore-last-of-role" if ipareplica_ignore_last_of_role | bool
else "" }}
register: result_uninstall
# 2 means that uninstall failed because IPA replica was not configured
failed_when: result_uninstall.rc != 0 and "'Env' object
has no attribute 'basedn'" not in result_uninstall.stderr
# IPA server is not configured on this system" not in
# result_uninstall.stdout_lines
changed_when: result_uninstall.rc == 0
# until: result_uninstall.rc == 0
retries: 2
delay: 1
- name: Set parameters
ansible.builtin.set_fact:
_ignore_topology_disconnect: "{{ ipaserver_ignore_topology_disconnect | default(ipareplica_ignore_topology_disconnect) | default(omit) }}"
_ignore_last_of_role: "{{ ipaserver_ignore_last_of_role | default(ipareplica_ignore_last_of_role) | default(omit) }}"
_remove_from_domain: "{{ ipaserver_remove_from_domain | default(ipareplica_remove_from_domain) | default(omit) }}"
_remove_on_server: "{{ ipaserver_remove_on_server | default(ipareplica_remove_on_server) | default(omit) }}"
#- name: Uninstall - Remove all replication agreements and data about replica
# command: >
# /usr/sbin/ipa-replica-manage
# del
# {{ ipareplica_hostname | default(ansible_facts['fqdn']) }}
# --force
# --password={{ ipadm_password }}
# failed_when: False
# delegate_to: "{{ groups.ipaserver[0] | default(fail) }}"
#- name: Remove IPA replica packages
# package:
# name: "{{ ipareplica_packages }}"
# state: absent
- name: Uninstall - Uninstall replica
ansible.builtin.include_role:
name: ipaserver
vars:
state: absent
ipaserver_ignore_topology_disconnect: "{{ _ignore_topology_disconnect | default(false) }}"
ipaserver_ignore_last_of_role: "{{ _ignore_last_of_role | default(false) }}"
ipaserver_remove_from_domain: "{{ _remove_from_domain | default(false) }}"
ipaserver_remove_on_server: "{{ _remove_on_server | default(NULL) }}"

View File

@@ -79,7 +79,7 @@ Example playbook to setup the IPA server using admin and dirman passwords from a
state: present
```
Example playbook to unconfigure the IPA client(s) using principal and password from inventory file:
Example playbook to unconfigure the IPA server using principal and password from inventory file:
```yaml
---
@@ -168,6 +168,64 @@ Server installation step 2: Copy `<ipaserver hostname>-chain.crt` to the IPA ser
The files can also be copied automatically: Set `ipaserver_copy_csr_to_controller` to true in the server installation step 1 and set `ipaserver_external_cert_files_from_controller` to point to the `chain.crt` file in the server installation step 2.
Since version 4.10, FreeIPA supports creating certificates using random serial numbers. Random serial numbers is a global and permanent setting, that can only be activated while deploying the first server of the domain. Replicas will inherit this setting automatically. An example of an inventory file to deploy a server with random serial numbers enabled is:
```ini
[ipaserver]
ipaserver.example.com
[ipaserver:vars]
ipaserver_domain=example.com
ipaserver_realm=EXAMPLE.COM
ipaadmin_password=MySecretPassword123
ipadm_password=MySecretPassword234
ipaserver_random_serial_number=true
```
By setting the variable in the inventory file, the same ipaserver deployment playbook, shown before, can be used.
Example inventory file to remove a server from the domain:
```ini
[ipaserver]
ipaserver.example.com
[ipaserver:vars]
ipaadmin_password=MySecretPassword123
ipaserver_remove_from_domain=true
```
Example playbook to remove an IPA server using admin passwords from the domain:
```yaml
---
- name: Playbook to remove IPA server
hosts: ipaserver
become: true
roles:
- role: ipaserver
state: absent
```
The inventory will enable the removal of the server (also a replica) from the domain. Additional options are needed if the removal of the server/replica is resulting in a topology disconnect or if the server/replica is the last that has a role.
To continue with the removal with a topology disconnect it is needed to set these parameters:
```ini
ipaserver_ignore_topology_disconnect=true
ipaserver_remove_on_server=ipaserver2.example.com
```
To continue with the removal for a server that is the last that has a role:
```ini
ipaserver_ignore_last_of_role=true
```
Be careful with enabling the `ipaserver_ignore_topology_disconnect` and especially `ipaserver_ignore_last_of_role`, the change can not be reverted easily.
Playbooks
=========
@@ -221,6 +279,7 @@ Variable | Description | Required
`ipaserver_no_ui_redirect` | Do not automatically redirect to the Web UI. (bool) | no
`ipaserver_dirsrv_config_file` | The path to LDIF file that will be used to modify configuration of dse.ldif during installation. (string) | no
`ipaserver_pki_config_override` | Path to ini file with config overrides. This is only usable with recent FreeIPA versions. (string) | no
`ipaserver_random_serial_numbers` | Enable use of random serial numbers for certificates. Requires FreeIPA version 4.10 or later. (boolean) | no
SSL certificate Variables
-------------------------
@@ -252,6 +311,7 @@ Variable | Description | Required
`ipaclient_no_ssh` | The bool value defines if OpenSSH client will be configured. `ipaclient_no_ssh` defaults to `no`. | no
`ipaclient_no_sshd` | The bool value defines if OpenSSH server will be configured. `ipaclient_no_sshd` defaults to `no`. | no
`ipaclient_no_sudo` | The bool value defines if SSSD will be configured as a data source for sudo. `ipaclient_no_sudo` defaults to `no`. | no
`ipaclient_subid` | The bool value defines if SSSD will be configured as a data source for subid. `ipaclient_subid` defaults to `no`. | no
`ipaclient_no_dns_sshfp` | The bool value defines if DNS SSHFP records will not be created automatically. `ipaclient_no_dns_sshfp` defaults to `no`. | no
Certificate system Variables
@@ -304,6 +364,19 @@ Variable | Description | Required
`ipaserver_external_cert_files_from_controller` | Files containing the IPA CA certificates and the external CA certificate chains on the controller that will be copied to the ipaserver host to `/root` folder. (list of string) | no
`ipaserver_copy_csr_to_controller` | Copy the generated CSR from the ipaserver to the controller as `"{{ inventory_hostname }}-ipa.csr"`. (bool) | no
Undeploy Variables (`state`: absent)
------------------------------------
These settings should only be used if the result is really wanted. The change might not be revertable easily.
Variable | Description | Required
-------- | ----------- | --------
`ipaserver_ignore_topology_disconnect` | If enabled this enforces the removal of the server even if it results in a topology disconnect. Be careful with this setting. (bool) | false
`ipaserver_ignore_last_of_role` | If enabled this enforces the removal of the server even if the server is the last with one that has a role. Be careful, this might not be revered easily. (bool) | false
`ipaserver_remove_from_domain` | This enables the removal of the server from the domain additionally to the undeployment. (bool) | false
`ipaserver_remove_on_server` | The value defines the server/replica in the domain that will to be used to remove the server/replica from the domain if `ipaserver_ignore_topology_disconnect` and `ipaserver_remove_from_domain` are enabled. Without the need to enable `ipaserver_ignore_topology_disconnect`, the value will be automatically detected using the replication agreements of the server/replica. (string) | false
Authors
=======

View File

@@ -11,6 +11,7 @@ ipaserver_no_hbac_allow: no
ipaserver_no_pkinit: no
ipaserver_no_ui_redirect: no
ipaserver_mem_check: yes
ipaserver_random_serial_numbers: false
### ssl certificate ###
### client ###
ipaclient_mkhomedir: no
@@ -42,3 +43,4 @@ ipaserver_copy_csr_to_controller: no
### uninstall ###
ipaserver_ignore_topology_disconnect: no
ipaserver_ignore_last_of_role: no
ipaserver_remove_from_domain: false

View File

@@ -0,0 +1,264 @@
# -*- coding: utf-8 -*-
# Authors:
# Thomas Woerner <twoerner@redhat.com>
#
# Copyright (C) 2019-2022 Red Hat
# see file 'COPYING' for use and warranty information
#
# This program is free software; you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>.
from __future__ import (absolute_import, division, print_function)
__metaclass__ = type
ANSIBLE_METADATA = {
"metadata_version": "1.0",
"supported_by": "community",
"status": ["preview"],
}
DOCUMENTATION = """
---
module: ipaserver_get_connected_server
short_description: Get connected servers for server
description: Get connected servers for server
options:
ipaadmin_principal:
description: The admin principal.
default: admin
type: str
ipaadmin_password:
description: The admin password.
required: true
type: str
hostname:
description: The FQDN server name.
type: str
required: true
author:
- Thomas Woerner (@t-woerner)
"""
EXAMPLES = """
"""
RETURN = """
server:
description: Connected server name
returned: always
type: str
"""
import os
import tempfile
import shutil
from contextlib import contextmanager
from ansible.module_utils.basic import AnsibleModule
from ansible.module_utils._text import to_text
from ansible.module_utils import six
try:
from ipalib import api
from ipalib import errors as ipalib_errors # noqa
from ipalib.config import Env
from ipaplatform.paths import paths
from ipapython.ipautil import run
from ipalib.constants import DEFAULT_CONFIG
try:
from ipalib.install.kinit import kinit_password
except ImportError:
from ipapython.ipautil import kinit_password
except ImportError as _err:
MODULE_IMPORT_ERROR = str(_err)
else:
MODULE_IMPORT_ERROR = None
if six.PY3:
unicode = str
def temp_kinit(principal, password):
"""Kinit with password using a temporary ccache."""
ccache_dir = tempfile.mkdtemp(prefix='krbcc')
ccache_name = os.path.join(ccache_dir, 'ccache')
try:
kinit_password(principal, password, ccache_name)
except RuntimeError as e:
raise RuntimeError("Kerberos authentication failed: %s" % str(e))
os.environ["KRB5CCNAME"] = ccache_name
return ccache_dir, ccache_name
def temp_kdestroy(ccache_dir, ccache_name):
"""Destroy temporary ticket and remove temporary ccache."""
if ccache_name is not None:
run([paths.KDESTROY, '-c', ccache_name], raiseonerr=False)
del os.environ['KRB5CCNAME']
if ccache_dir is not None:
shutil.rmtree(ccache_dir, ignore_errors=True)
@contextmanager
def ipa_connect(module, principal=None, password=None):
"""
Create a context with a connection to IPA API.
Parameters
----------
module: AnsibleModule
The AnsibleModule to use
principal: string
The optional principal name
password: string
The admin password.
"""
if not password:
module.fail_json(msg="Password is required.")
if not principal:
principal = "admin"
ccache_dir = None
ccache_name = None
try:
ccache_dir, ccache_name = temp_kinit(principal, password)
# api_connect start
env = Env()
env._bootstrap()
env._finalize_core(**dict(DEFAULT_CONFIG))
api.bootstrap(context="server", debug=env.debug, log=None)
api.finalize()
if api.env.in_server:
backend = api.Backend.ldap2
else:
backend = api.Backend.rpcclient
if not backend.isconnected():
backend.connect(ccache=ccache_name)
# api_connect end
except Exception as e:
module.fail_json(msg=str(e))
else:
try:
yield ccache_name
except Exception as e:
module.fail_json(msg=str(e))
finally:
temp_kdestroy(ccache_dir, ccache_name)
def ipa_command(command, name, args):
"""
Execute an IPA API command with a required `name` argument.
Parameters
----------
command: string
The IPA API command to execute.
name: string
The name parameter to pass to the command.
args: dict
The parameters to pass to the command.
"""
return api.Command[command](name, **args)
def _afm_convert(value):
if value is not None:
if isinstance(value, list):
return [_afm_convert(x) for x in value]
if isinstance(value, dict):
return {_afm_convert(k): _afm_convert(v)
for k, v in value.items()}
if isinstance(value, str):
return to_text(value)
return value
def module_params_get(module, name):
return _afm_convert(module.params.get(name))
def host_show(module, name):
_args = {
"all": True,
}
try:
_result = ipa_command("host_show", name, _args)
except ipalib_errors.NotFound as e:
msg = str(e)
if "host not found" in msg:
return None
module.fail_json(msg="host_show failed: %s" % msg)
return _result["result"]
def main():
module = AnsibleModule(
argument_spec=dict(
ipaadmin_principal=dict(type="str", default="admin"),
ipaadmin_password=dict(type="str", required=True, no_log=True),
hostname=dict(type="str", required=True),
),
supports_check_mode=True,
)
if MODULE_IMPORT_ERROR is not None:
module.fail_json(msg=MODULE_IMPORT_ERROR)
# In check mode always return changed.
if module.check_mode:
module.exit_json(changed=False)
ipaadmin_principal = module_params_get(module, "ipaadmin_principal")
ipaadmin_password = module_params_get(module, "ipaadmin_password")
hostname = module_params_get(module, "hostname")
server = None
right_left = ["iparepltoposegmentrightnode", "iparepltoposegmentleftnode"]
with ipa_connect(module, ipaadmin_principal, ipaadmin_password):
# At first search in the domain, then ca suffix:
# Search for the first iparepltoposegmentleftnode (node 2), where
# iparepltoposegmentrightnode is hostname (node 1), then for the
# first iparepltoposegmentrightnode (node 2) where
# iparepltoposegmentleftnode is hostname (node 1).
for suffix_name in ["domain", "ca"]:
for node1, node2 in [[right_left[0], right_left[1]],
[right_left[1], right_left[0]]]:
args = {node1: hostname}
result = api.Command.topologysegment_find(
suffix_name, **args)
if result and "result" in result and len(result["result"]) > 0:
res = result["result"][0]
if node2 in res:
if len(res[node2]) > 0:
server = res[node2][0]
break
if server is not None:
module.exit_json(changed=False, server=server)
module.exit_json(changed=False)
if __name__ == "__main__":
main()

View File

@@ -208,6 +208,10 @@ options:
description: The installer ca_subject setting
type: str
required: no
random_serial_numbers:
description: The installer random_serial_numbers setting
type: bool
required: no
allow_zone_overlap:
description: Create DNS zone even if it already exists
type: bool
@@ -304,7 +308,7 @@ from ansible.module_utils.ansible_ipa_server import (
check_dirsrv, ScriptError, get_fqdn, verify_fqdn, BadHostError,
validate_domain_name, load_pkcs12, IPA_PYTHON_VERSION,
encode_certificate, check_available_memory, getargspec, adtrustinstance,
get_min_idstart
get_min_idstart, SerialNumber
)
from ansible.module_utils import six
@@ -369,6 +373,8 @@ def main():
elements='str', default=None),
subject_base=dict(required=False, type='str'),
ca_subject=dict(required=False, type='str'),
random_serial_numbers=dict(required=False, type='bool',
default=False),
# ca_signing_algorithm
# dns
allow_zone_overlap=dict(required=False, type='bool',
@@ -456,6 +462,8 @@ def main():
'external_cert_files')
options.subject_base = ansible_module.params.get('subject_base')
options.ca_subject = ansible_module.params.get('ca_subject')
options._random_serial_numbers = ansible_module.params.get(
'random_serial_numbers')
# ca_signing_algorithm
# dns
options.allow_zone_overlap = ansible_module.params.get(
@@ -513,6 +521,12 @@ def main():
ansible_module.fail_json(
msg="pki_config_override: %s" % str(e))
# Check if Random Serial Numbers v3 is available
if options._random_serial_numbers and SerialNumber is None:
ansible_module.fail_json(
msg="Random Serial Numbers is not supported for this IPA version"
)
# default values ########################################################
# idstart and idmax
@@ -1147,42 +1161,45 @@ def main():
pkinit_pkcs12_info = ("/etc/ipa/.tmp_pkcs12_pkinit", pkinit_pin)
pkinit_ca_cert = encode_certificate(pkinit_ca_cert)
ansible_module.exit_json(changed=False,
ipa_python_version=IPA_PYTHON_VERSION,
# basic
domain=options.domain_name,
realm=realm_name,
hostname=host_name,
_hostname_overridden=bool(options.host_name),
no_host_dns=options.no_host_dns,
# server
setup_adtrust=options.setup_adtrust,
setup_kra=options.setup_kra,
setup_ca=options.setup_ca,
idstart=options.idstart,
idmax=options.idmax,
no_pkinit=options.no_pkinit,
# ssl certificate
_dirsrv_pkcs12_info=dirsrv_pkcs12_info,
_dirsrv_ca_cert=dirsrv_ca_cert,
_http_pkcs12_info=http_pkcs12_info,
_http_ca_cert=http_ca_cert,
_pkinit_pkcs12_info=pkinit_pkcs12_info,
_pkinit_ca_cert=pkinit_ca_cert,
# certificate system
external_ca=options.external_ca,
external_ca_type=options.external_ca_type,
external_ca_profile=options.external_ca_profile,
# ad trust
rid_base=options.rid_base,
secondary_rid_base=options.secondary_rid_base,
# client
ntp_servers=options.ntp_servers,
ntp_pool=options.ntp_pool,
# additional
_installation_cleanup=_installation_cleanup,
domainlevel=options.domainlevel,
sid_generation_always=sid_generation_always)
ansible_module.exit_json(
changed=False,
ipa_python_version=IPA_PYTHON_VERSION,
# basic
domain=options.domain_name,
realm=realm_name,
hostname=host_name,
_hostname_overridden=bool(options.host_name),
no_host_dns=options.no_host_dns,
# server
setup_adtrust=options.setup_adtrust,
setup_kra=options.setup_kra,
setup_ca=options.setup_ca,
idstart=options.idstart,
idmax=options.idmax,
no_pkinit=options.no_pkinit,
# ssl certificate
_dirsrv_pkcs12_info=dirsrv_pkcs12_info,
_dirsrv_ca_cert=dirsrv_ca_cert,
_http_pkcs12_info=http_pkcs12_info,
_http_ca_cert=http_ca_cert,
_pkinit_pkcs12_info=pkinit_pkcs12_info,
_pkinit_ca_cert=pkinit_ca_cert,
# certificate system
external_ca=options.external_ca,
external_ca_type=options.external_ca_type,
external_ca_profile=options.external_ca_profile,
# ad trust
rid_base=options.rid_base,
secondary_rid_base=options.secondary_rid_base,
# client
ntp_servers=options.ntp_servers,
ntp_pool=options.ntp_pool,
# additional
_installation_cleanup=_installation_cleanup,
domainlevel=options.domainlevel,
sid_generation_always=sid_generation_always,
random_serial_numbers=options._random_serial_numbers,
)
if __name__ == '__main__':

View File

@@ -6,15 +6,15 @@ galaxy_info:
description: A role to setup an iPA domain server
company: Red Hat, Inc
license: GPLv3
min_ansible_version: 2.8
min_ansible_version: "2.8"
platforms:
- name: Fedora
versions:
- all
- name: EL
versions:
- 7
- 8
- "7"
- "8"
galaxy_tags:
- identity
- ipa

View File

@@ -44,7 +44,7 @@ __all__ = ["IPAChangeConf", "certmonger", "sysrestore", "root_logger",
"check_available_memory", "getargspec", "get_min_idstart",
"paths", "api", "ipautil", "adtrust_imported", "NUM_VERSION",
"time_service", "kra_imported", "dsinstance", "IPA_PYTHON_VERSION",
"NUM_VERSION"]
"NUM_VERSION", "SerialNumber"]
import sys
import logging
@@ -203,6 +203,13 @@ try:
except ImportError:
get_min_idstart = None
# SerialNumber is defined in versions 4.10 and later and is
# used by Random Serian Number v3.
try:
from ipalib.parameters import SerialNumber
except ImportError:
SerialNumber = None
else:
# IPA version < 4.5

View File

@@ -1,14 +1,18 @@
---
- name: Install - Initialize ipaserver_external_cert_files
set_fact:
ipaserver_external_cert_files: []
when: ipaserver_external_cert_files is undefined
- name: Install - Copy "{{ item }}" "{{ inventory_hostname }}':/root/'{{ item | basename }}"
copy:
src: "{{ item }}"
dest: "/root/{{ item | basename }}"
mode: preserve
force: yes
- name: Install - Extend ipaserver_external_cert_files with "/root/{{ item | basename }}"
set_fact:
ipaserver_external_cert_files: "{{ ipaserver_external_cert_files + [ '/root/' + (item | basename) ] }}"
- name: Copy external certificates
vars:
__item_basename: "{{ item | basename }}"
block:
- name: Install - Initialize ipaserver_external_cert_files
ansible.builtin.set_fact:
ipaserver_external_cert_files: []
when: ipaserver_external_cert_files is undefined
- name: Install - Copy "{{ item + " " + inventory_hostname + ':/root/' + __item_basename }}"
ansible.builtin.copy:
src: "{{ item }}"
dest: "/root/{{ __item_basename }}"
mode: preserve
force: yes
- name: Install - Extend ipaserver_external_cert_files with "/root/{{ __item_basename }}"
ansible.builtin.set_fact:
ipaserver_external_cert_files: "{{ ipaserver_external_cert_files + ['/root/' + (__item_basename)] }}"

View File

@@ -1,57 +1,59 @@
---
# tasks file for ipaserver
- block:
- name: Install - Package installation
when: ipaserver_install_packages | bool
block:
- name: Install - Ensure that IPA server packages are installed
package:
ansible.builtin.package:
name: "{{ ipaserver_packages }}"
state: present
- name: Install - Ensure that IPA server packages for dns are installed
package:
ansible.builtin.package:
name: "{{ ipaserver_packages_dns }}"
state: present
when: ipaserver_setup_dns | bool
- name: Install - Ensure that IPA server packages for adtrust are installed
package:
ansible.builtin.package:
name: "{{ ipaserver_packages_adtrust }}"
state: present
when: ipaserver_setup_adtrust | bool
- name: Install - Ensure that firewall packages installed
package:
ansible.builtin.package:
name: "{{ ipaserver_packages_firewalld }}"
state: present
when: ipaserver_setup_firewalld | bool
when: ipaserver_install_packages | bool
- block:
- name: Install - Firewall configuration
when: ipaserver_setup_firewalld | bool
block:
- name: Firewalld service - Ensure that firewalld is running
systemd:
ansible.builtin.systemd:
name: firewalld
enabled: yes
state: started
- name: Firewalld - Verify runtime zone "{{ ipaserver_firewalld_zone }}"
shell: >
ansible.builtin.shell: >
firewall-cmd
--info-zone="{{ ipaserver_firewalld_zone }}"
>/dev/null
when: ipaserver_firewalld_zone is defined
- name: Firewalld - Verify permanent zone "{{ ipaserver_firewalld_zone }}"
shell: >
ansible.builtin.shell: >
firewall-cmd
--permanent
--info-zone="{{ ipaserver_firewalld_zone }}"
>/dev/null
when: ipaserver_firewalld_zone is defined
when: ipaserver_setup_firewalld | bool
- include_tasks: "{{ role_path }}/tasks/copy_external_cert.yml"
- name: Copy external certs
ansible.builtin.include_tasks: "{{ role_path }}/tasks/copy_external_cert.yml"
with_items: "{{ ipaserver_external_cert_files_from_controller }}"
when: ipaserver_external_cert_files_from_controller is defined and
ipaserver_external_cert_files_from_controller|length > 0 and
@@ -106,6 +108,7 @@
external_cert_files: "{{ ipaserver_external_cert_files | default(omit) }}"
subject_base: "{{ ipaserver_subject_base | default(omit) }}"
ca_subject: "{{ ipaserver_ca_subject | default(omit) }}"
random_serial_numbers: "{{ ipaserver_random_serial_numbers | default(omit) }}"
# ca_signing_algorithm
### dns ###
allow_zone_overlap: "{{ ipaserver_allow_zone_overlap }}"
@@ -127,14 +130,15 @@
### additional ###
register: result_ipaserver_test
- block:
# This block is executed only when
# not ansible_check_mode and
# not (not result_ipaserver_test.changed and
# (result_ipaserver_test.client_already_configured is defined or
# result_ipaserver_test.server_already_configured is defined)
- block:
- name: Install - Deploy server
when: not ansible_check_mode and not
(not result_ipaserver_test.changed and
(result_ipaserver_test.client_already_configured is defined or
result_ipaserver_test.server_already_configured is defined))
block:
- name: Install - Obtain master password
when: ipaserver_master_password is undefined
block:
- name: Install - Master password creation
no_log: yes
ipaserver_master_password:
@@ -144,11 +148,15 @@
- name: Install - Use new master password
no_log: yes
set_fact:
ipaserver_master_password:
ansible.builtin.set_fact:
__derived_master_password:
"{{ result_ipaserver_master_password.password }}"
when: ipaserver_master_password is undefined
- name: Use user defined master password, if provided
when: ipaserver_master_password is defined
no_log: yes
ansible.builtin.set_fact:
__derived_master_password: "{{ ipaserver_master_password }}"
- name: Install - Server preparation
ipaserver_prepare:
@@ -192,7 +200,7 @@
### additional ###
setup_ca: "{{ result_ipaserver_test.setup_ca }}"
sid_generation_always: "{{ result_ipaserver_test.sid_generation_always }}"
random_serial_numbers: no
random_serial_numbers: "{{ result_ipaserver_test.random_serial_numbers }}"
_hostname_overridden: "{{ result_ipaserver_test._hostname_overridden }}"
register: result_ipaserver_prepare
@@ -207,7 +215,7 @@
ipaserver_setup_ds:
dm_password: "{{ ipadm_password }}"
password: "{{ ipaadmin_password }}"
# master_password: "{{ ipaserver_master_password }}"
# master_password: "{{ __derived_master_password }}"
domain: "{{ result_ipaserver_test.domain }}"
realm: "{{ result_ipaserver_test.realm | default(omit) }}"
hostname: "{{ result_ipaserver_test.hostname }}"
@@ -236,7 +244,7 @@
ipaserver_setup_krb:
dm_password: "{{ ipadm_password }}"
password: "{{ ipaadmin_password }}"
master_password: "{{ ipaserver_master_password }}"
master_password: "{{ __derived_master_password }}"
domain: "{{ result_ipaserver_test.domain }}"
realm: "{{ result_ipaserver_test.realm }}"
hostname: "{{ result_ipaserver_test.hostname }}"
@@ -269,7 +277,7 @@
ipaserver_setup_ca:
dm_password: "{{ ipadm_password }}"
password: "{{ ipaadmin_password }}"
master_password: "{{ ipaserver_master_password }}"
master_password: "{{ __derived_master_password }}"
# ip_addresses: "{{ result_ipaserver_prepare.ip_addresses }}"
domain: "{{ result_ipaserver_test.domain }}"
realm: "{{ result_ipaserver_test.realm }}"
@@ -307,15 +315,17 @@
_http_ca_cert: "{{ result_ipaserver_test._http_ca_cert }}"
register: result_ipaserver_setup_ca
- name: Copy /root/ipa.csr to "{{ inventory_hostname }}-ipa.csr"
fetch:
- name: Copy /root/ipa.csr to "{{ inventory_hostname + '-ipa.csr' }}"
ansible.builtin.fetch:
src: /root/ipa.csr
dest: "{{ inventory_hostname }}-ipa.csr"
flat: yes
when: result_ipaserver_setup_ca.csr_generated | bool and
ipaserver_copy_csr_to_controller | bool
- block:
- name: Install - Configure services
when: not result_ipaserver_setup_ca.csr_generated | bool
block:
- name: Install - Setup otpd
ipaserver_setup_otpd:
realm: "{{ result_ipaserver_test.realm }}"
@@ -326,7 +336,7 @@
ipaserver_setup_http:
dm_password: "{{ ipadm_password }}"
password: "{{ ipaadmin_password }}"
master_password: "{{ ipaserver_master_password }}"
master_password: "{{ __derived_master_password }}"
domain: "{{ result_ipaserver_test.domain }}"
realm: "{{ result_ipaserver_test.realm }}"
hostname: "{{ result_ipaserver_test.hostname }}"
@@ -416,7 +426,7 @@
_dirsrv_pkcs12_info: "{{ result_ipaserver_test._dirsrv_pkcs12_info if result_ipaserver_test._dirsrv_pkcs12_info != None else omit }}"
- name: Install - Setup client
include_role:
ansible.builtin.include_role:
name: ipaclient
vars:
state: present
@@ -437,14 +447,8 @@
setup_ca: "{{ result_ipaserver_test.setup_ca }}"
register: result_ipaserver_enable_ipa
- name: Install - Cleanup root IPA cache
file:
path: "/root/.ipa_cache"
state: absent
when: result_ipaserver_enable_ipa.changed
- name: Install - Configure firewalld
command: >
ansible.builtin.command: >
firewall-cmd
--permanent
--zone="{{ ipaserver_firewalld_zone if ipaserver_firewalld_zone is
@@ -458,7 +462,7 @@
when: ipaserver_setup_firewalld | bool
- name: Install - Configure firewalld runtime
command: >
ansible.builtin.command: >
firewall-cmd
--zone="{{ ipaserver_firewalld_zone if ipaserver_firewalld_zone is
defined else '' }}"
@@ -470,19 +474,17 @@
{{ "--add-service=ntp" if not ipaclient_no_ntp | bool else "" }}
when: ipaserver_setup_firewalld | bool
when: not result_ipaserver_setup_ca.csr_generated | bool
always:
- name: Install - Cleanup root IPA cache
ansible.builtin.file:
path: "/root/.ipa_cache"
state: absent
- name: Cleanup temporary files
file:
ansible.builtin.file:
path: "{{ item }}"
state: absent
with_items:
- "/etc/ipa/.tmp_pkcs12_dirsrv"
- "/etc/ipa/.tmp_pkcs12_http"
- "/etc/ipa/.tmp_pkcs12_pkinit"
when: not ansible_check_mode and not
(not result_ipaserver_test.changed and
(result_ipaserver_test.client_already_configured is defined or
result_ipaserver_test.server_already_configured is defined))

View File

@@ -2,7 +2,7 @@
# tasks file for ipaserver
- name: Import variables specific to distribution
include_vars: "{{ item }}"
ansible.builtin.include_vars: "{{ item }}"
with_first_found:
- "vars/{{ ansible_facts['distribution'] }}-{{ ansible_facts['distribution_version'] }}.yml"
- "vars/{{ ansible_facts['distribution'] }}-{{ ansible_facts['distribution_major_version'] }}.yml"
@@ -17,9 +17,9 @@
- "vars/default.yml"
- name: Install IPA server
include_tasks: install.yml
ansible.builtin.include_tasks: install.yml
when: state|default('present') == 'present'
- name: Uninstall IPA server
include_tasks: uninstall.yml
ansible.builtin.include_tasks: uninstall.yml
when: state|default('present') == 'absent'

View File

@@ -1,8 +1,49 @@
---
# tasks to uninstall IPA server
- name: Uninstall - Set server hostname for removal
ansible.builtin.set_fact:
_remove_hostname: "{{ ansible_facts['fqdn'] }}"
- name: Uninstall - Remove server
when: ipaserver_remove_from_domain
block:
- name: Uninstall - Fail on missing ipaadmin_password for server removal
ansible.builtin.fail:
msg: "'ipaadmin_password' is needed for 'ipaserver_remove_from_domain'"
when: ipaadmin_password is not defined
- name: Uninstall - Fail on missing ipaserver_remove_on_server with ipaserver_ignore_topology_disconnect
ansible.builtin.fail:
msg: "'ipaserver_remove_on_server' is needed for 'ipaserver_remove_from_domain' with 'ipaserver_ignore_topology_disconnect'"
when: ipaserver_ignore_topology_disconnect | bool
and ipaserver_remove_on_server is not defined
- name: Uninstall - Get connected server
ipaserver_get_connected_server:
ipaadmin_principal: "{{ ipaadmin_principal | default('admin') }}"
ipaadmin_password: "{{ ipaadmin_password }}"
hostname: "{{ _remove_hostname }}"
register: result_get_connected_server
when: ipaserver_remove_on_server is not defined
# REMOVE SERVER FROM DOMAIN
- name: Uninstall - Server del "{{ _remove_hostname }}"
ipaserver:
ipaadmin_principal: "{{ ipaadmin_principal | default('admin') }}"
ipaadmin_password: "{{ ipaadmin_password }}"
name: "{{ _remove_hostname }}"
ignore_last_of_role: "{{ ipaserver_ignore_last_of_role }}"
ignore_topology_disconnect: "{{ ipaserver_ignore_topology_disconnect }}"
# delete_continue: "{{ ipaserver_delete_continue }}"
state: absent
delegate_to: "{{ ipaserver_remove_on_server | default(result_get_connected_server.server) }}"
when: ipaserver_remove_on_server is defined or
result_get_connected_server.server is defined
- name: Uninstall - Uninstall IPA server
command: >
ansible.builtin.command: >
/usr/sbin/ipa-server-install
--uninstall
-U
@@ -15,6 +56,6 @@
changed_when: uninstall.rc == 0
#- name: Remove IPA server packages
# package:
# ansible.builtin.package:
# name: "{{ ipaserver_packages }}"
# state: absent

View File

@@ -6,15 +6,15 @@ galaxy_info:
description: A role to setup IPA server(s) for Smart Card authentication
company: Red Hat, Inc
license: GPLv3
min_ansible_version: 2.8
min_ansible_version: "2.8"
platforms:
- name: Fedora
versions:
- all
- name: EL
versions:
- 7
- 8
- "7"
- "8"
galaxy_tags:
- identity
- ipa

View File

@@ -2,7 +2,8 @@
# tasks file for ipasmartcard_client role
- name: Uninstall smartcard client
ansible.builtin.fail: msg="Uninstalling smartcard for IPA is not supported"
ansible.builtin.fail:
msg: "Uninstalling smartcard for IPA is not supported"
when: state|default('present') == 'absent'
- name: Import variables specific to distribution
@@ -20,7 +21,8 @@
# If neither distro nor family is supported, try a default configuration.
- "vars/default.yml"
- block:
- name: Client configuration
block:
# CA CERTS
@@ -35,7 +37,8 @@
# Fail on empty "ipasmartcard_client_ca_certs"
- name: Fail on empty "ipasmartcard_client_ca_certs"
ansible.builtin.fail: msg="No CA certs given in 'ipasmartcard_client_ca_certs'"
ansible.builtin.fail:
msg: "No CA certs given in 'ipasmartcard_client_ca_certs'"
when: ipasmartcard_client_ca_certs is not defined or
ipasmartcard_client_ca_certs | length < 1
@@ -67,13 +70,13 @@
ipaadmin_principal: admin
when: ipaadmin_principal is undefined
- name: kinit using "{{ ipaadmin_principal }}" password
- name: Authenticate using kinit with password for "{{ ipaadmin_principal }}"
ansible.builtin.command: kinit "{{ ipaadmin_principal }}"
args:
stdin: "{{ ipaadmin_password }}"
when: ipaadmin_password is defined
- name: kinit using "{{ ipaadmin_principal }}" keytab
- name: Authenticate using kinit with keytab for "{{ ipaadmin_principal }}"
ansible.builtin.command: kinit -kt "{{ ipaadmin_keytab }}" "{{ ipaadmin_principal }}"
when: ipaadmin_keytab is defined
@@ -99,7 +102,9 @@
# Ensure /etc/sssd/pki exists
- block:
- name: Prepare for authselect
when: ipasmartcard_client_vars.USE_AUTHSELECT
block:
- name: Ensure /etc/sssd/pki exists
ansible.builtin.file:
path: /etc/sssd/pki
@@ -111,8 +116,6 @@
path: /etc/sssd/pki/sssd_auth_ca_db.pem
state: absent
when: ipasmartcard_client_vars.USE_AUTHSELECT
# Upload smartcard CA certificates to systemwide db
- name: Upload smartcard CA certificates to systemwide db
@@ -169,5 +172,5 @@
### ALWAYS ###
always:
- name: kdestroy
- name: Destroy Kerberos tickets
ansible.builtin.command: kdestroy -A

View File

@@ -6,15 +6,15 @@ galaxy_info:
description: A role to setup IPA server(s) for Smart Card authentication
company: Red Hat, Inc
license: GPLv3
min_ansible_version: 2.8
min_ansible_version: "2.8"
platforms:
- name: Fedora
versions:
- all
- name: EL
versions:
- 7
- 8
- "7"
- "8"
galaxy_tags:
- identity
- ipa

View File

@@ -2,7 +2,8 @@
# tasks file for ipasmartcard_server role
- name: Uninstall smartcard server
ansible.builtin.fail: msg="Uninstalling smartcard for IPA is not supported"
ansible.builtin.fail:
msg: "Uninstalling smartcard for IPA is not supported"
when: state|default('present') == 'absent'
- name: Import variables specific to distribution
@@ -20,13 +21,15 @@
# If neither distro nor family is supported, try a default configuration.
- "vars/default.yml"
- block:
- name: Server configuration
block:
# CA CERTS
# Fail on empty "ipasmartcard_server_ca_certs"
- name: Fail on empty "ipasmartcard_server_ca_certs"
ansible.builtin.fail: msg="No CA certs given in 'ipasmartcard_server_ca_certs'"
ansible.builtin.fail:
msg: "No CA certs given in 'ipasmartcard_server_ca_certs'"
when: ipasmartcard_server_ca_certs is not defined or
ipasmartcard_server_ca_certs | length < 1
@@ -39,7 +42,7 @@
# INSTALL bind-utils
- name: Ensure {{ ipasmartcard_server_bindutils_packages }} are installed
- name: Ensure bind utilities packages are installed
ansible.builtin.package:
name: "{{ ipasmartcard_server_bindutils_packages }}"
state: present
@@ -52,13 +55,13 @@
ipaadmin_principal: admin
when: ipaadmin_principal is undefined
- name: kinit using "{{ ipaadmin_principal }}" password
- name: Athenticate with kinit and password for "{{ ipaadmin_principal }}"
ansible.builtin.command: kinit "{{ ipaadmin_principal }}"
args:
stdin: "{{ ipaadmin_password }}"
when: ipaadmin_password is defined
- name: kinit using "{{ ipaadmin_principal }}" keytab
- name: Authenticate with kinit and keytab for "{{ ipaadmin_principal }}"
ansible.builtin.command: kinit -kt "{{ ipaadmin_keytab }}" "{{ ipaadmin_principal }}"
when: ipaadmin_keytab is defined
@@ -69,12 +72,13 @@
register: result_ipa_server_show
- name: Fail if not an IPA server
ansible.builtin.fail: msg="Not an IPA server"
ansible.builtin.fail:
msg: "Not an IPA server"
when: result_ipa_server_show.failed
- name: Get Domain from server-find server name
ansible.builtin.set_fact:
ipaserver_domain: "{{ (result_ipa_server_show.stdout | regex_search('cn: (.+)', '\\1'))[0].split('.')[1:] | join ('.') }}"
ipaserver_domain: "{{ (result_ipa_server_show.stdout | regex_search('cn: (.+)', '\\1'))[0].split('.')[1:] | join('.') }}"
when: ipaserver_domain is not defined
- name: Get ipa-ca records
@@ -82,7 +86,8 @@
register: result_get_ipaca_records
- name: Fail if ipa-ca records are not resolvable
ansible.builtin.fail: msg="ipa-ca records are not resolvable"
ansible.builtin.fail:
msg: "ipa-ca records are not resolvable"
when: result_get_ipaca_records.failed or
result_get_ipaca_records.stdout | length == 0
@@ -162,10 +167,11 @@
# HTTPD IFP
- block:
- name: Allow HTTPD ifp
when: ipasmartcard_server_vars.allow_httpd_ifp
block:
# Allow Apache to access SSSD IFP
- name: Allow Apache to access SSSD IFP
ansible.builtin.command: "{{ ipasmartcard_server_vars.python_interpreter }}"
args:
@@ -186,11 +192,11 @@
name: sssd
state: restarted
when: ipasmartcard_server_vars.allow_httpd_ifp
# Ensure /etc/sssd/pki exists
- block:
- name: Prepare for authselect
when: ipasmartcard_server_vars.USE_AUTHSELECT
block:
- name: Ensure /etc/sssd/pki exists
ansible.builtin.file:
path: /etc/sssd/pki
@@ -202,8 +208,6 @@
path: /etc/sssd/pki/sssd_auth_ca_db.pem
state: absent
when: ipasmartcard_server_vars.USE_AUTHSELECT
# Upload smartcard CA certificates to systemwide db
- name: Upload smartcard CA certificates to systemwide db
@@ -243,5 +247,5 @@
### ALWAYS ###
always:
- name: kdestroy
- name: Destroy Kereberos tickets
ansible.builtin.command: kdestroy -A

View File

@@ -6,7 +6,7 @@
tasks:
- name: Include FreeIPA facts.
include_tasks: ../env_freeipa_facts.yml
ansible.builtin.include_tasks: ../env_freeipa_facts.yml
# Test will only be executed if host is not a server.
- name: Execute with server context in the client.
@@ -28,13 +28,13 @@
# in upstream CI.
- name: Test automember using client context, in client host.
import_playbook: test_automember.yml
ansible.builtin.import_playbook: test_automember.yml
when: groups['ipaclients']
vars:
ipa_test_host: ipaclients
- name: Test automember using client context, in server host.
import_playbook: test_automember.yml
ansible.builtin.import_playbook: test_automember.yml
when: groups['ipaclients'] is not defined or not groups['ipaclients']
vars:
ipa_context: client

View File

@@ -8,9 +8,9 @@
# SET FACTS
- name: Get Domain from server name
set_fact:
ansible.builtin.set_fact:
ipaserver_domain: "{{ ansible_facts['fqdn'].split('.')[1:] |
join ('.') }}"
join('.') }}"
when: ipaserver_domain is not defined
# CLEANUP TEST ITEMS

View File

@@ -8,9 +8,9 @@
# SET FACTS
- name: Get Domain from server name
set_fact:
ansible.builtin.set_fact:
ipaserver_domain: "{{ ansible_facts['fqdn'].split('.')[1:] |
join ('.') }}"
join('.') }}"
when: ipaserver_domain is not defined
# CLEANUP TEST ITEMS

View File

@@ -5,18 +5,18 @@
gather_facts: no
tasks:
- name: ensure test location TestLocation is present
- name: Ensure test location TestLocation is present
ipaautomountlocation:
ipaadmin_password: SomeADMINpassword
name: TestLocation
- name: ensure test map TestMap is present
- name: Ensure test map TestMap is present
ipaautomountmap:
ipaadmin_password: SomeADMINpassword
name: TestMap
location: TestLocation
- name: ensure key NewKeyName is absent
- name: Ensure key NewKeyName is absent
ipaautomountkey:
ipaadmin_password: SomeADMINpassword
location: TestLocation
@@ -24,7 +24,7 @@
key: NewKeyName
state: absent
- name: ensure key TestKey is absent
- name: Ensure key TestKey is absent
ipaautomountkey:
ipaadmin_password: SomeADMINpassword
location: TestLocation
@@ -32,9 +32,10 @@
key: NewKeyName
state: absent
- block:
- name: Execute Automount Key tests
block:
### test the key creation, and modification
- name: ensure key TestKey is present
- name: Ensure key TestKey is present
ipaautomountkey:
ipaadmin_password: SomeADMINpassword
location: TestLocation
@@ -45,7 +46,7 @@
register: result
failed_when: result.failed or not result.changed
- name: ensure key TestKey is present again
- name: Ensure key TestKey is present again
ipaautomountkey:
ipaadmin_password: SomeADMINpassword
location: TestLocation
@@ -57,7 +58,7 @@
failed_when: result.failed or result.changed
## modify the key
- name: ensure key TestKey information has been updated
- name: Ensure key TestKey information has been updated
ipaautomountkey:
ipaadmin_password: SomeADMINpassword
location: TestLocation
@@ -68,7 +69,7 @@
register: result
failed_when: result.failed or not result.changed
- name: ensure key TestKey information has been updated again
- name: Ensure key TestKey information has been updated again
ipaautomountkey:
ipaadmin_password: SomeADMINpassword
location: TestLocation
@@ -80,7 +81,7 @@
failed_when: result.failed or result.changed
## modify the name
- name: ensure key TestKey has been renamed to NewKeyName
- name: Ensure key TestKey has been renamed to NewKeyName
ipaautomountkey:
ipaadmin_password: SomeADMINpassword
location: TestLocation
@@ -91,7 +92,7 @@
register: result
failed_when: result.failed or not result.changed
- name: ensure key TestKey is absent
- name: Ensure key TestKey is absent
ipaautomountkey:
ipaadmin_password: SomeADMINpassword
location: TestLocation
@@ -101,7 +102,7 @@
register: result
failed_when: result.failed or result.changed
- name: ensure key NewKeyName is present
- name: Ensure key NewKeyName is present
ipaautomountkey:
ipaadmin_password: SomeADMINpassword
location: TestLocation
@@ -112,7 +113,7 @@
register: result
failed_when: result.failed or result.changed
- name: ensure failure when state is renamed and newname is not set
- name: Ensure failure when state is renamed and newname is not set
ipaautomountkey:
ipaadmin_password: SomeADMINpassword
location: TestLocation
@@ -124,7 +125,7 @@
### cleanup after the tests
always:
- name: ensure key NewKeyName is absent
- name: Ensure key NewKeyName is absent
ipaautomountkey:
ipaadmin_password: SomeADMINpassword
location: TestLocation
@@ -132,7 +133,7 @@
key: NewKeyName
state: absent
- name: ensure key TestKey is absent
- name: Ensure key TestKey is absent
ipaautomountkey:
ipaadmin_password: SomeADMINpassword
location: TestLocation
@@ -140,14 +141,14 @@
key: NewKeyName
state: absent
- name: ensure map TestMap is absent
- name: Ensure map TestMap is absent
ipaautomountmap:
ipaadmin_password: SomeADMINpassword
name: TestMap
location: TestLocation
state: absent
- name: ensure location TestLocation is absent
- name: Ensure location TestLocation is absent
ipaautomountlocation:
ipaadmin_password: SomeADMINpassword
name: TestLocation

View File

@@ -6,7 +6,7 @@
tasks:
- name: Include FreeIPA facts.
include_tasks: ../env_freeipa_facts.yml
ansible.builtin.include_tasks: ../env_freeipa_facts.yml
# Test will only be executed if host is not a server.
- name: Execute with server context in the client.
@@ -29,13 +29,13 @@
# in upstream CI.
- name: Test automountlocation using client context, in client host.
import_playbook: test_automountkey.yml
ansible.builtin.import_playbook: test_automountkey.yml
when: groups['ipaclients']
vars:
ipa_test_host: ipaclients
- name: Test automountlocation using client context, in server host.
import_playbook: test_automountkey.yml
ansible.builtin.import_playbook: test_automountkey.yml
when: groups['ipaclients'] is not defined or not groups['ipaclients']
vars:
ipa_context: client

View File

@@ -5,7 +5,7 @@
gather_facts: false
tasks:
- name: ensure automountlocation TestLocations are absent before testing
- name: Ensure automountlocation TestLocations are absent before testing
ipaautomountlocation:
ipaadmin_password: SomeADMINpassword
ipaapi_context: "{{ ipa_context | default(omit) }}"
@@ -14,7 +14,7 @@
- TestLocation_02
state: absent
- name: ensure empty automountlocation does nothing
- name: Ensure empty automountlocation does nothing
ipaautomountlocation:
ipaadmin_password: SomeADMINpassword
ipaapi_context: "{{ ipa_context | default(omit) }}"
@@ -23,7 +23,7 @@
register: result
failed_when: not result.failed or "At least one location must be provided" not in result.msg
- name: ensure empty automountlocation does nothing on absent
- name: Ensure empty automountlocation does nothing on absent
ipaautomountlocation:
ipaadmin_password: SomeADMINpassword
ipaapi_context: "{{ ipa_context | default(omit) }}"
@@ -32,7 +32,7 @@
register: result
failed_when: not result.failed or "At least one location must be provided" not in result.msg
- name: ensure automountlocation TestLocation is present
- name: Ensure automountlocation TestLocation is present
ipaautomountlocation:
ipaadmin_password: SomeADMINpassword
ipaapi_context: "{{ ipa_context | default(omit) }}"
@@ -41,7 +41,7 @@
register: result
failed_when: not result.changed or result.failed
- name: ensure automountlocation TestLocation is present again
- name: Ensure automountlocation TestLocation is present again
ipaautomountlocation:
ipaadmin_password: SomeADMINpassword
ipaapi_context: "{{ ipa_context | default(omit) }}"
@@ -50,7 +50,7 @@
register: result
failed_when: result.changed or result.failed
- name: ensure automountlocation TestLocation is absent
- name: Ensure automountlocation TestLocation is absent
ipaautomountlocation:
ipaadmin_password: SomeADMINpassword
ipaapi_context: "{{ ipa_context | default(omit) }}"
@@ -59,7 +59,7 @@
register: result
failed_when: not result.changed or result.failed
- name: ensure automountlocation TestLocation is absent again
- name: Ensure automountlocation TestLocation is absent again
ipaautomountlocation:
ipaadmin_password: SomeADMINpassword
ipaapi_context: "{{ ipa_context | default(omit) }}"
@@ -68,7 +68,7 @@
register: result
failed_when: result.changed or result.failed
- name: ensure a list of automountlocations are present
- name: Ensure a list of automountlocations are present
ipaautomountlocation:
ipaadmin_password: SomeADMINpassword
ipaapi_context: "{{ ipa_context | default(omit) }}"
@@ -79,7 +79,7 @@
register: result
failed_when: result.failed or not result.changed
- name: ensure a list of automountlocations exist
- name: Ensure a list of automountlocations exist
ipaautomountlocation:
ipaadmin_password: SomeADMINpassword
ipaapi_context: "{{ ipa_context | default(omit) }}"
@@ -90,7 +90,7 @@
register: result
failed_when: result.changed or result.failed
- name: ensure a list of automountlocations are absent
- name: Ensure a list of automountlocations are absent
ipaautomountlocation:
ipaadmin_password: SomeADMINpassword
ipaapi_context: "{{ ipa_context | default(omit) }}"
@@ -101,7 +101,7 @@
register: result
failed_when: result.failed or not result.changed
- name: ensure multiple automountlocations are absent
- name: Ensure multiple automountlocations are absent
ipaautomountlocation:
ipaadmin_password: SomeADMINpassword
ipaapi_context: "{{ ipa_context | default(omit) }}"

View File

@@ -6,7 +6,7 @@
tasks:
- name: Include FreeIPA facts.
include_tasks: ../env_freeipa_facts.yml
ansible.builtin.include_tasks: ../env_freeipa_facts.yml
# Test will only be executed if host is not a server.
- name: Execute with server context in the client.
@@ -27,13 +27,13 @@
# in upstream CI.
- name: Test automountlocation using client context, in client host.
import_playbook: test_automountlocation.yml
ansible.builtin.import_playbook: test_automountlocation.yml
when: groups['ipaclients']
vars:
ipa_test_host: ipaclients
- name: Test automountlocation using client context, in server host.
import_playbook: test_automountlocation.yml
ansible.builtin.import_playbook: test_automountlocation.yml
when: groups['ipaclients'] is not defined or not groups['ipaclients']
vars:
ipa_context: client

View File

@@ -6,7 +6,7 @@
tasks:
# setup environment
- name: ensure test maps are absent
- name: Ensure test maps are absent
ipaautomountmap:
ipaadmin_password: SomeADMINpassword
name:
@@ -15,28 +15,29 @@
location: TestLocation
state: absent
- name: ensure location TestLocation is absent
- name: Ensure location TestLocation is absent
ipaautomountlocation:
ipaadmin_password: SomeADMINpassword
name: TestLocation
state: absent
- name: ensure map TestMap is absent
- name: Ensure map TestMap is absent
ipaautomountmap:
ipaadmin_password: SomeADMINpassword
name: TestMap
location: TestLocation
state: absent
- name: ensure location TestLocation is present
- name: Ensure location TestLocation is present
ipaautomountlocation:
ipaadmin_password: SomeADMINpassword
name: TestLocation
state: present
# TESTS
- block:
- name: ensure map TestMap is present
- name: Execute Automount Map tests
block:
- name: Ensure map TestMap is present
ipaautomountmap:
ipaadmin_password: SomeADMINpassword
name: TestMap
@@ -45,7 +46,7 @@
register: result
failed_when: result.failed or not result.changed
- name: ensure map TestMap is present again
- name: Ensure map TestMap is present again
ipaautomountmap:
ipaadmin_password: SomeADMINpassword
name: TestMap
@@ -53,7 +54,7 @@
register: result
failed_when: result.failed or result.changed
- name: ensure map TestMap has a different description
- name: Ensure map TestMap has a different description
ipaautomountmap:
ipaadmin_password: SomeADMINpassword
name: TestMap
@@ -62,7 +63,7 @@
register: result
failed_when: result.failed or not result.changed
- name: ensure map TestMap has a different description, again
- name: Ensure map TestMap has a different description, again
ipaautomountmap:
ipaadmin_password: SomeADMINpassword
name: TestMap
@@ -71,7 +72,7 @@
register: result
failed_when: result.failed or result.changed
- name: ensure map TestMap has an empty description
- name: Ensure map TestMap has an empty description
ipaautomountmap:
ipaadmin_password: SomeADMINpassword
name: TestMap
@@ -80,7 +81,7 @@
register: result
failed_when: result.failed or not result.changed
- name: ensure map TestMap has an empty description, again
- name: Ensure map TestMap has an empty description, again
ipaautomountmap:
ipaadmin_password: SomeADMINpassword
name: TestMap
@@ -89,7 +90,7 @@
register: result
failed_when: result.failed or result.changed
- name: ensure map TestMap is removed
- name: Ensure map TestMap is removed
ipaautomountmap:
ipaadmin_password: SomeADMINpassword
name: TestMap
@@ -98,7 +99,7 @@
register: result
failed_when: result.failed or not result.changed
- name: ensure map TestMap has been removed
- name: Ensure map TestMap has been removed
ipaautomountmap:
ipaadmin_password: SomeADMINpassword
name: TestMap
@@ -107,7 +108,7 @@
register: result
failed_when: result.failed or result.changed
- name: ensure map TestMap01 is present
- name: Ensure map TestMap01 is present
ipaautomountmap:
ipaadmin_password: SomeADMINpassword
name: TestMap01
@@ -116,7 +117,7 @@
register: result
failed_when: result.failed or not result.changed
- name: ensure map TestMap02 is present
- name: Ensure map TestMap02 is present
ipaautomountmap:
ipaadmin_password: SomeADMINpassword
name: TestMap02
@@ -125,7 +126,7 @@
register: result
failed_when: result.failed or not result.changed
- name: ensure TestMap01 and TestMap02 are both absent
- name: Ensure TestMap01 and TestMap02 are both absent
ipaautomountmap:
ipaadmin_password: SomeADMINpassword
name:
@@ -136,7 +137,7 @@
register: result
failed_when: result.failed or not result.changed
- name: ensure TestMap01 and TestMap02 are both absent again
- name: Ensure TestMap01 and TestMap02 are both absent again
ipaautomountmap:
ipaadmin_password: SomeADMINpassword
name:
@@ -149,7 +150,7 @@
# CLEAN UP
always:
- name: ensure test maps are absent
- name: Ensure test maps are absent
ipaautomountmap:
ipaadmin_password: SomeADMINpassword
name:
@@ -158,7 +159,7 @@
location: TestLocation
state: absent
- name: ensure location TestLocation is absent
- name: Ensure location TestLocation is absent
ipaautomountlocation:
ipaadmin_password: SomeADMINpassword
name: TestLocation

View File

@@ -6,7 +6,7 @@
tasks:
- name: Include FreeIPA facts.
include_tasks: ../env_freeipa_facts.yml
ansible.builtin.include_tasks: ../env_freeipa_facts.yml
# Test will only be executed if host is not a server.
- name: Execute with server context in the client.
@@ -28,13 +28,13 @@
# in upstream CI.
- name: Test automountmap using client context, in client host.
import_playbook: test_automountmap.yml
ansible.builtin.import_playbook: test_automountmap.yml
when: groups['ipaclients']
vars:
ipa_test_host: ipaclients
- name: Test automountmap using client context, in server host.
import_playbook: test_automountmap.yml
ansible.builtin.import_playbook: test_automountmap.yml
when: groups['ipaclients'] is not defined or not groups['ipaclients']
vars:
ipa_context: client

View File

@@ -9,6 +9,17 @@ stages:
# Fedora
- stage: Fedora_Ansible_Latest
dependsOn: []
jobs:
- template: templates/group_tests.yml
parameters:
build_number: $(Build.BuildNumber)
scenario: fedora-latest
ansible_version: "-core"
# Fedora
- stage: Fedora_Latest
dependsOn: []
jobs:
@@ -16,7 +27,7 @@ stages:
parameters:
build_number: $(Build.BuildNumber)
scenario: fedora-latest
ansible_version: "-core >=2.12,<2.13"
ansible_version: "-core >=2.13,<2.14"
# Galaxy on Fedora
@@ -27,7 +38,7 @@ stages:
parameters:
build_number: $(Build.BuildNumber)
scenario: fedora-latest
ansible_version: "-core >=2.12,<2.13"
ansible_version: "-core >=2.13,<2.14"
# CentOS 9 Stream
@@ -38,7 +49,7 @@ stages:
parameters:
build_number: $(Build.BuildNumber)
scenario: c9s
ansible_version: "-core >=2.12,<2.13"
ansible_version: "-core >=2.13,<2.14"
# CentOS 8 Stream
@@ -49,7 +60,7 @@ stages:
parameters:
build_number: $(Build.BuildNumber)
scenario: c8s
ansible_version: "-core >=2.12,<2.13"
ansible_version: "-core >=2.13,<2.14"
# CentOS 7
@@ -60,4 +71,4 @@ stages:
parameters:
build_number: $(Build.BuildNumber)
scenario: centos-7
ansible_version: "-core >=2.12,<2.13"
ansible_version: "-core >=2.13,<2.14"

View File

@@ -16,15 +16,6 @@ stages:
# Fedora
- stage: FedoraLatest_Ansible_Core_2_11
dependsOn: []
jobs:
- template: templates/group_tests.yml
parameters:
build_number: $(Build.BuildNumber)
scenario: fedora-latest
ansible_version: "-core >=2.11,<2.12"
- stage: FedoraLatest_Ansible_Core_2_12
dependsOn: []
jobs:
@@ -34,6 +25,24 @@ stages:
scenario: fedora-latest
ansible_version: "-core >=2.12,<2.13"
- stage: FedoraLatest_Ansible_Core_2_13
dependsOn: []
jobs:
- template: templates/group_tests.yml
parameters:
build_number: $(Build.BuildNumber)
scenario: fedora-latest
ansible_version: "-core >=2.13,<2.14"
- stage: FedoraLatest_Ansible_Core_2_14
dependsOn: []
jobs:
- template: templates/group_tests.yml
parameters:
build_number: $(Build.BuildNumber)
scenario: fedora-latest
ansible_version: "-core >=2.14,<2.15"
- stage: FedoraLatest_Ansible_latest
dependsOn: []
jobs:
@@ -43,26 +52,8 @@ stages:
scenario: fedora-latest
ansible_version: ""
- stage: FedoraLatest_Ansible_Core_latest
dependsOn: []
jobs:
- template: templates/group_tests.yml
parameters:
build_number: $(Build.BuildNumber)
scenario: fedora-latest
ansible_version: "-core"
# Galaxy on Fedora
- stage: Galaxy_FedoraLatest_Ansible_Core_2_11
dependsOn: []
jobs:
- template: templates/galaxy_tests.yml
parameters:
build_number: $(Build.BuildNumber)
scenario: fedora-latest
ansible_version: "-core >=2.11,<2.12"
- stage: Galaxy_FedoraLatest_Ansible_Core_2_12
dependsOn: []
jobs:
@@ -72,6 +63,24 @@ stages:
scenario: fedora-latest
ansible_version: "-core >=2.12,<2.13"
- stage: Galaxy_FedoraLatest_Ansible_Core_2_13
dependsOn: []
jobs:
- template: templates/galaxy_tests.yml
parameters:
build_number: $(Build.BuildNumber)
scenario: fedora-latest
ansible_version: "-core >=2.13,<2.14"
- stage: Galaxy_FedoraLatest_Ansible_Core_2_14
dependsOn: []
jobs:
- template: templates/galaxy_tests.yml
parameters:
build_number: $(Build.BuildNumber)
scenario: fedora-latest
ansible_version: "-core >=2.14,<2.15"
- stage: Galaxy_FedoraLatest_Ansible_latest
dependsOn: []
jobs:
@@ -81,26 +90,8 @@ stages:
scenario: fedora-latest
ansible_version: ""
- stage: Galaxy_FedoraLatest_Ansible_Core_latest
dependsOn: []
jobs:
- template: templates/galaxy_tests.yml
parameters:
build_number: $(Build.BuildNumber)
scenario: fedora-latest
ansible_version: "-core"
# Fedora Rawhide
- stage: FedoraRawhide_Ansible_Core_2_11
dependsOn: []
jobs:
- template: templates/group_tests.yml
parameters:
build_number: $(Build.BuildNumber)
scenario: fedora-rawhide
ansible_version: "-core >=2.11,<2.12"
- stage: FedoraRawhide_Ansible_Core_2_12
dependsOn: []
jobs:
@@ -110,6 +101,24 @@ stages:
scenario: fedora-rawhide
ansible_version: "-core >=2.12,<2.13"
- stage: FedoraRawhide_Ansible_Core_2_13
dependsOn: []
jobs:
- template: templates/group_tests.yml
parameters:
build_number: $(Build.BuildNumber)
scenario: fedora-rawhide
ansible_version: "-core >=2.13,<2.14"
- stage: FedoraRawhide_Ansible_Core_2_14
dependsOn: []
jobs:
- template: templates/group_tests.yml
parameters:
build_number: $(Build.BuildNumber)
scenario: fedora-rawhide
ansible_version: "-core >=2.14,<2.15"
- stage: FedoraRawhide_Ansible_latest
dependsOn: []
jobs:
@@ -119,26 +128,8 @@ stages:
scenario: fedora-rawhide
ansible_version: ""
- stage: FedoraRawhide_Ansible_Core_latest
dependsOn: []
jobs:
- template: templates/group_tests.yml
parameters:
build_number: $(Build.BuildNumber)
scenario: fedora-rawhide
ansible_version: "-core"
# CentoOS 9 Stream
- stage: c9s_Ansible_Core_2_11
dependsOn: []
jobs:
- template: templates/group_tests.yml
parameters:
build_number: $(Build.BuildNumber)
scenario: c9s
ansible_version: "-core >=2.11,<2.12"
- stage: c9s_Ansible_Core_2_12
dependsOn: []
jobs:
@@ -148,6 +139,24 @@ stages:
scenario: c9s
ansible_version: "-core >=2.12,<2.13"
- stage: c9s_Ansible_Core_2_13
dependsOn: []
jobs:
- template: templates/group_tests.yml
parameters:
build_number: $(Build.BuildNumber)
scenario: c9s
ansible_version: "-core >=2.13,<2.14"
- stage: c9s_Ansible_Core_2_14
dependsOn: []
jobs:
- template: templates/group_tests.yml
parameters:
build_number: $(Build.BuildNumber)
scenario: c9s
ansible_version: "-core >=2.14,<2.15"
- stage: c9s_Ansible_latest
dependsOn: []
jobs:
@@ -157,26 +166,8 @@ stages:
scenario: c9s
ansible_version: ""
- stage: c9s_Ansible_Core_latest
dependsOn: []
jobs:
- template: templates/group_tests.yml
parameters:
build_number: $(Build.BuildNumber)
scenario: c9s
ansible_version: "-core"
# CentOS 8 Stream
- stage: c8s_Ansible_Core_2_11
dependsOn: []
jobs:
- template: templates/group_tests.yml
parameters:
build_number: $(Build.BuildNumber)
scenario: c8s
ansible_version: "-core >=2.11,<2.12"
- stage: c8s_Ansible_Core_2_12
dependsOn: []
jobs:
@@ -186,6 +177,24 @@ stages:
scenario: c8s
ansible_version: "-core >=2.12,<2.13"
- stage: c8s_Ansible_Core_2_13
dependsOn: []
jobs:
- template: templates/group_tests.yml
parameters:
build_number: $(Build.BuildNumber)
scenario: c8s
ansible_version: "-core >=2.13,<2.14"
- stage: c8s_Ansible_Core_2_14
dependsOn: []
jobs:
- template: templates/group_tests.yml
parameters:
build_number: $(Build.BuildNumber)
scenario: c8s
ansible_version: "-core >=2.14,<2.15"
- stage: c8s_Ansible_latest
dependsOn: []
jobs:
@@ -195,26 +204,8 @@ stages:
scenario: c8s
ansible_version: ""
- stage: c8s_Ansible_Core_latest
dependsOn: []
jobs:
- template: templates/group_tests.yml
parameters:
build_number: $(Build.BuildNumber)
scenario: c8s
ansible_version: "-core"
# CentOS 7
- stage: CentOS7_Ansible_Core_2_11
dependsOn: []
jobs:
- template: templates/group_tests.yml
parameters:
build_number: $(Build.BuildNumber)
scenario: centos-7
ansible_version: "-core >=2.11,<2.12"
- stage: CentOS7_Ansible_Core_2_12
dependsOn: []
jobs:
@@ -224,6 +215,24 @@ stages:
scenario: centos-7
ansible_version: "-core >=2.12,<2.13"
- stage: CentOS7_Ansible_Core_2_13
dependsOn: []
jobs:
- template: templates/group_tests.yml
parameters:
build_number: $(Build.BuildNumber)
scenario: centos-7
ansible_version: "-core >=2.13,<2.14"
- stage: CentOS7_Ansible_Core_2_14
dependsOn: []
jobs:
- template: templates/group_tests.yml
parameters:
build_number: $(Build.BuildNumber)
scenario: centos-7
ansible_version: "-core >=2.14,<2.15"
- stage: CentOS7_Ansible_latest
dependsOn: []
jobs:
@@ -232,12 +241,3 @@ stages:
build_number: $(Build.BuildNumber)
scenario: centos-7
ansible_version: ""
- stage: CentOS7_Ansible_Core_latest
dependsOn: []
jobs:
- template: templates/group_tests.yml
parameters:
build_number: $(Build.BuildNumber)
scenario: centos-7
ansible_version: "-core"

View File

@@ -38,7 +38,7 @@ stages:
parameters:
build_number: $(Build.BuildNumber)
scenario: c9s
ansible_version: "-core >=2.12,<2.13"
ansible_version: "-core >=2.13,<2.14"
# CentOS 8 Stream
@@ -49,7 +49,7 @@ stages:
parameters:
build_number: $(Build.BuildNumber)
scenario: c8s
ansible_version: "-core >=2.12,<2.13"
ansible_version: "-core >=2.13,<2.14"
# CentOS 7
@@ -60,7 +60,7 @@ stages:
parameters:
build_number: $(Build.BuildNumber)
scenario: centos-7
ansible_version: "-core >=2.12,<2.13"
ansible_version: "-core >=2.13,<2.14"
# Rawhide
@@ -71,4 +71,4 @@ stages:
parameters:
build_number: $(Build.BuildNumber)
scenario: fedora-rawhide
ansible_version: "-core >=2.12,<2.13"
ansible_version: "-core >=2.13,<2.14"

View File

@@ -19,12 +19,15 @@ jobs:
versionSpec: '${{ parameters.python_version }}'
- script: python -m pip install --upgrade pip setuptools wheel ansible
retryCountOnTaskFailure: 5
displayName: Install tools
- script: pip install molecule[docker]
retryCountOnTaskFailure: 5
displayName: Install molecule
- script: molecule create -s ${{ parameters.build_scenario_name }}
retryCountOnTaskFailure: 5
displayName: Create test container
env:
ANSIBLE_LIBRARY: ./molecule

View File

@@ -15,7 +15,7 @@ parameters:
jobs:
- job: Test_PyTests
displayName: Run pytests on ${{ parameters.scenario }}
timeoutInMinutes: 120
timeoutInMinutes: 240
steps:
- task: UsePythonVersion@0
inputs:
@@ -25,16 +25,21 @@ jobs:
pip install \
"molecule[docker]>=3" \
"ansible${{ parameters.ansible_version }}"
retryCountOnTaskFailure: 5
displayName: Install molecule and Ansible
- script: ansible-galaxy collection install community.docker ansible.posix
retryCountOnTaskFailure: 5
displayName: Install Ansible collections
- script: pip install -r requirements-tests.txt
retryCountOnTaskFailure: 5
displayName: Install dependencies
- script: |
utils/build-galaxy-release.sh -i
molecule create -s ${{ parameters.scenario }}
retryCountOnTaskFailure: 5
displayName: Setup test container
env:
ANSIBLE_LIBRARY: ./molecule

View File

@@ -22,7 +22,7 @@ parameters:
jobs:
- job: Test_Group${{ parameters.group_number }}
displayName: Run playbook tests ${{ parameters.scenario }} (${{ parameters.group_number }}/${{ parameters.number_of_groups }})
timeoutInMinutes: 120
timeoutInMinutes: 240
variables:
- template: variables.yaml
- template: variables_${{ parameters.scenario }}.yaml
@@ -35,17 +35,21 @@ jobs:
pip install \
"molecule[docker]>=3" \
"ansible${{ parameters.ansible_version }}"
retryCountOnTaskFailure: 5
displayName: Install molecule and Ansible
- script: ansible-galaxy collection install community.docker ansible.posix
retryCountOnTaskFailure: 5
displayName: Install Ansible collections
- script: pip install -r requirements-tests.txt
retryCountOnTaskFailure: 5
displayName: Install dependencies
- script: |
utils/build-galaxy-release.sh -i
molecule create -s ${{ parameters.scenario }}
retryCountOnTaskFailure: 5
displayName: Setup test container
env:
ANSIBLE_LIBRARY: ./molecule

View File

@@ -21,7 +21,7 @@ parameters:
jobs:
- job: Test_Group${{ parameters.group_number }}
displayName: Run playbook tests ${{ parameters.scenario }} (${{ parameters.group_number }}/${{ parameters.number_of_groups }})
timeoutInMinutes: 120
timeoutInMinutes: 240
variables:
- template: variables.yaml
- template: variables_${{ parameters.scenario }}.yaml
@@ -34,20 +34,25 @@ jobs:
pip install \
"molecule[docker]>=3" \
"ansible${{ parameters.ansible_version }}"
retryCountOnTaskFailure: 5
displayName: Install molecule and Ansible
- script: ansible-galaxy collection install community.docker ansible.posix
retryCountOnTaskFailure: 5
displayName: Install Ansible collections
- script: pip install -r requirements-tests.txt
retryCountOnTaskFailure: 5
displayName: Install dependencies
- script: |
rm -rf ~/ansible
mkdir -p ~/.ansible/roles ~/.ansible/library ~/.ansible/module_utils
cp -a roles/* ~/.ansible/roles
cp -a plugins/modules/* ~/.ansible/library
cp -a plugins/module_utils/* ~/.ansible/module_utils
molecule create -s ${{ parameters.scenario }}
retryCountOnTaskFailure: 5
displayName: Setup test container
env:
ANSIBLE_LIBRARY: ./molecule

View File

@@ -21,7 +21,7 @@ parameters:
jobs:
- job: Test_Group${{ parameters.group_number }}
displayName: Run playbook tests ${{ parameters.scenario }} (${{ parameters.group_number }}/${{ parameters.number_of_groups }})
timeoutInMinutes: 120
timeoutInMinutes: 240
variables:
- template: variables.yaml
- template: variables_${{ parameters.scenario }}.yaml
@@ -34,20 +34,25 @@ jobs:
pip install \
"molecule[docker]>=3" \
"ansible${{ parameters.ansible_version }}"
retryCountOnTaskFailure: 5
displayName: Install molecule and Ansible
- script: ansible-galaxy collection install community.docker ansible.posix
retryCountOnTaskFailure: 5
displayName: Install Ansible collections
- script: pip install -r requirements-tests.txt
retryCountOnTaskFailure: 5
displayName: Install dependencies
- script: |
rm -rf ~/ansible
mkdir -p ~/.ansible/roles ~/.ansible/library ~/.ansible/module_utils
cp -a roles/* ~/.ansible/roles
cp -a plugins/modules/* ~/.ansible/library
cp -a plugins/module_utils/* ~/.ansible/module_utils
molecule create -s ${{ parameters.scenario }}
retryCountOnTaskFailure: 5
displayName: Setup test container
env:
ANSIBLE_LIBRARY: ./molecule

View File

@@ -15,7 +15,7 @@ parameters:
jobs:
- job: Test_PyTests
displayName: Run pytests on ${{ parameters.scenario }}
timeoutInMinutes: 120
timeoutInMinutes: 240
variables:
- template: variables.yaml
- template: variables_${{ parameters.scenario }}.yaml
@@ -28,20 +28,25 @@ jobs:
pip install \
"molecule[docker]>=3" \
"ansible${{ parameters.ansible_version }}"
retryCountOnTaskFailure: 5
displayName: Install molecule and Ansible
- script: ansible-galaxy collection install community.docker ansible.posix
retryCountOnTaskFailure: 5
displayName: Install Ansible collections
- script: pip install -r requirements-tests.txt
retryCountOnTaskFailure: 5
displayName: Install dependencies
- script: |
rm -rf ~/.ansible
mkdir -p ~/.ansible/roles ~/.ansible/library ~/.ansible/module_utils
cp -a roles/* ~/.ansible/roles
cp -a plugins/modules/* ~/.ansible/library
cp -a plugins/module_utils/* ~/.ansible/module_utils
molecule create -s ${{ parameters.scenario }}
retryCountOnTaskFailure: 5
displayName: Setup test container
env:
ANSIBLE_LIBRARY: ./molecule

Some files were not shown because too many files have changed in this diff Show More