Compare commits

...

343 Commits

Author SHA1 Message Date
Rafael Guterres Jeffman
16a4eb81ce Merge pull request #1264 from t-woerner/module_documentation_fixes
Documentation fixes for issues found by ansible-test part of ansible-core 2.17.1
2024-07-01 09:31:47 -03:00
Thomas Woerner
cd16490531 Role modules: Docs: Fix default value for string list parameters
Fix default values for string list parameters where default is missing
in the DOCUMENTATION section, but is defined in argument_specs.
2024-07-01 11:33:28 +02:00
Thomas Woerner
7b6bc32fa0 tests/utils.py: Fix missing whitespace around arithmetic operator (E226) 2024-07-01 11:31:13 +02:00
Thomas Woerner
6b3fb78db6 ipareplica_prepare: Documentation: Fixed name of ipa_client_installed
The ipa_client_installed option was named as client_configured in the
DOCUMENTATION section.
2024-07-01 11:17:52 +02:00
Thomas Woerner
67df9e83c7 ipaclient_setup_nss: Documentation: Add default for selinux_works
The default value for selinux_works was missing in the DOCUMENTATION
section.
2024-07-01 11:15:49 +02:00
Thomas Woerner
14be339af0 service: Docs: Fix required for name, add delete_continue to services
DOCUMENTATOIN section: Fix required for name, add missing
delete_continue to services option.
2024-07-01 10:39:04 +02:00
Thomas Woerner
76251ead2c idp: Drop no_log from docs section, allow to log token_uri and keys_uri
The no_log tag is only allowed in the argument_spec, but not in the
DOCUMENTATION section. Set no_log=False for token_uri and
keys_uri=keys_uri in the argument_spec to enforce logging.
2024-07-01 10:33:04 +02:00
Thomas Woerner
74028bd36c idoverrideuser: Docs: Fix sshpubkey element type, nomembers type
The element type for sshpubkey was using 'element' instead of 'elements'
also the type for nomembers was wrong in the DOCUMENTATION section.
2024-07-01 10:30:57 +02:00
Thomas Woerner
43217b9e70 cert: Fix short_description tag, add chain option, remove authors
Several fixes for the DOCUMENTATION section: The short_description tag
was 'short description', the chain option was missing and the unknown
authers tag has been removed.
2024-07-01 10:25:11 +02:00
Thomas Woerner
96209f6945 inventory/freeipa: Documentation: Fix version_added and drop plugin_type
In the freeipa inventoty plugin, fix version to 1.13.0 and drop unknown
tag plugin_type.
2024-07-01 10:23:21 +02:00
Thomas Woerner
7eac30127a ipamodule_base_docs: Documentation: Fix default for delete_continue
The default setting for delete_continue was True instead of true for
delete_continue in the DOCUMENTATION section.
2024-07-01 10:20:21 +02:00
Rafael Guterres Jeffman
719d1cd056 Merge pull request #1263 from t-woerner/tests_sanity_setuptools
tests/sanity/sanity.sh: Install setuptools with pip
2024-06-28 12:29:42 -03:00
Rafael Guterres Jeffman
832d44d986 Merge pull request #1262 from t-woerner/user_fix_idp_user_id_aliases
user: Fix idp_user_id aliases
2024-06-28 12:29:13 -03:00
Rafael Guterres Jeffman
82f403c0de Merge pull request #1261 from t-woerner/inventory_plugin_try_imports
plugins/inventory/freeipa: Try imports for requests and urllib3
2024-06-28 12:27:06 -03:00
Rafael Guterres Jeffman
fa4a90e628 Merge pull request #1259 from t-woerner/permission_DN_parameters_idempotency_fixes
permission: Fix idempotency issues for DN parameters
2024-06-28 12:26:45 -03:00
Rafael Guterres Jeffman
c38ff9b78c Merge pull request #1255 from t-woerner/service_readme_multi_services
README-service.md: Add multi service handling
2024-06-28 12:24:40 -03:00
Rafael Guterres Jeffman
85b1c54ce1 Merge pull request #1250 from t-woerner/convert_input_certificates
Convert input certificates
2024-06-28 12:24:07 -03:00
Rafael Guterres Jeffman
6d5f3f3274 Merge pull request #1248 from t-woerner/fix_batch_errors
ansible_freeipa_module: Fix errors in batch mode
2024-06-28 12:23:39 -03:00
Rafael Guterres Jeffman
1dba4ba408 Merge pull request #1246 from t-woerner/ipa_4_12_fixes
Fixes for FreeIPA 4.12
2024-06-28 12:23:20 -03:00
Thomas Woerner
e867373fc0 tests/sanity/sanity.sh: Install setuptools with pip
setuptools might not be installed before importing and using
galaxy_importer. This could result in a backtrace by disabling
ANSIBLE_TEST_LOCAL_IMAGE in galaxy-importer.cfg to run latest tests.
2024-06-28 17:08:16 +02:00
Thomas Woerner
c5c8cb3b04 user: Fix idp_user_id aliases
The alias for idp_user_id was ipaidpconfiglink by mistake. It was
already correct (ipaidpsub) in the DOCUMENTATION section and also
in the README.
2024-06-28 17:02:24 +02:00
Thomas Woerner
8944999657 service: Add multi service examples to EXAMPLES
The EXAMPLES section only contained a very simple example for multi
service handling. The examples from the README have been added.
2024-06-28 16:55:56 +02:00
Thomas Woerner
b7a04bc49b README-service.md: Add multi service handling
The service READNE so far lacks the documentation of multi service
handling within a single task.

The alias for the continue parameter was also added.

Fixes: #1113
2024-06-28 16:55:40 +02:00
Thomas Woerner
935bef4b9f Merge pull request #1130 from rjeffman/ansible_support_2_14_plus
Bump minimum supported Ansible version
2024-06-28 16:48:27 +02:00
Thomas Woerner
8e139e2fe9 plugins/inventory/freeipa: Try imports for requests and urllib3
The bindings for requests and urllib3 might not be available, especially
in the ansible-test fake execution test (next version). These imports are
now in a try exception clause to make sure that the fake execution test
will be passing and also that there is a better error message if the
bindings are missing.

urllib3.exceptions.InsecureRequestWarning is now also only disabled if
no certificate has been given for the verification of the connection.
2024-06-28 16:35:44 +02:00
Rafael Guterres Jeffman
332d41dc46 ansible-freeipa.spec: Bump minimum supported Ansible version to 2.15
ansible-freeipa roles do not work with Ansible 2.8 anymore, so the
minimum supported version is changed to 2.15, the oldest supported
Ansible version as of today.

This patch updates the minimum supported Ansible version and the list
of available modules.
2024-06-28 10:51:44 -03:00
Rafael Guterres Jeffman
ab94ff07a0 utils/templates: Bump minimum supported Ansible version to 2.15
ansible-freeipa roles do not work with Ansible 2.8 anymore, so the
minimum supported version is changed to 2.15, the oldest supported
Ansible version as of today.
2024-06-28 10:51:44 -03:00
Rafael Guterres Jeffman
5a5b3c1655 ipasmartcard_*: Bump minimum supported Ansible version to 2.15
ansible-freeipa roles do not work with Ansible 2.8 anymore, so the
minimum supported version is changed to 2.15, the oldest supported
Ansible version as of today.
2024-06-28 10:51:44 -03:00
Rafael Guterres Jeffman
74663b877a ipabackup: Bump minimum supported Ansible version to 2.15
ansible-freeipa roles do not work with Ansible 2.8 anymore, so the
minimum supported version is changed to 2.15, the oldest supported
Ansible version as of today.
2024-06-28 10:51:44 -03:00
Rafael Guterres Jeffman
2f06f194f1 ipaserver: Bump minimum supported Ansible version to 2.15
ansible-freeipa roles do not work with Ansible 2.8 anymore, so the
minimum supported version is changed to 2.15, the oldest supported
Ansible version as of today.
2024-06-28 10:51:44 -03:00
Rafael Guterres Jeffman
3148c10480 ipareplica: Bump minimum supported Ansible version to 2.15
ansible-freeipa roles do not work with Ansible 2.8 anymore, so the
minimum supported version is changed to 2.15, the oldest supported
Ansible version as of today.
2024-06-28 10:51:44 -03:00
Rafael Guterres Jeffman
f4187a1453 ipaclient: Bump minimum supported Ansible version to 2.15
ansible-freeipa roles do not work with Ansible 2.8 anymore, so the
minimum supported version is changed to 2.15, the oldest supported
Ansible version as of today.
2024-06-28 10:51:44 -03:00
Rafael Guterres Jeffman
7126dec0f3 README-*: Bump minimum Ansible supported version to 2.15
ansible-freeipa roles do not work with Ansible 2.8 anymore, so the
minimum supported version is changed to 2.15, the oldest supported
Ansible version as of today.

This patch includes the change to the version number in the collection
and all plugin README files. The collection README was also update to
remove text that related only to previous Ansible versions.
2024-06-28 10:51:44 -03:00
Rafael Guterres Jeffman
3d241e55b4 Set collection ansible-core minimum version to 2.15 2024-06-28 10:51:27 -03:00
Thomas Woerner
173acf282b permission: Fix idempotency issues for DN parameters
The parameters

- subtree (ipapermlocation)
- target (ipapermtarget)
- targetto (ipapermtargetto)
- targetfrom (ipapermtargetfrom)

have not been idempotent as the result returned from permission_show was
a DN and not a string.

The find_permission function has been exetended to convert the values
for these parameters to strings.

Fixes: #1257
2024-06-26 14:57:28 +02:00
Thomas Woerner
39ba225784 ansible_freeipa_module: Fix errors in batch mode
The error string returned by execute_ipa_commands in batch mode
additionally contains the whole parameter list for the command. This is
different to non batch mode execution and breaks tests that are checking
the returned error message.

A left over debug message also have been removed from the error
processing.
2024-06-25 17:31:45 +02:00
Thomas Woerner
b7ccd8fed5 ipauser: Use new convert_input_certificates
Certificates given by ansible could have leading and trailing white
space, but also multi line input is possible that also could have
leading and training white space and newlines.
2024-06-25 16:07:05 +02:00
Thomas Woerner
ef94b703df ipaidoverrideusere: Use new convert_input_certificates
Certificates given by ansible could have leading and trailing white
space, but also multi line input is possible that also could have
leading and training white space and newlines.
2024-06-25 16:07:05 +02:00
Thomas Woerner
0dc58be3f6 ipahost: Use new convert_input_certificates
Certificates given by ansible could have leading and trailing white
space, but also multi line input is possible that also could have
leading and training white space and newlines.
2024-06-25 16:07:05 +02:00
Thomas Woerner
b64da1dbb7 ipaservice: Use new convert_input_certificates
Certificates given by ansible could have leading and trailing white
space, but also multi line input is possible that also could have
leading and training white space and newlines.
2024-06-25 16:07:05 +02:00
Thomas Woerner
84b5d33c62 ansible_freeipa_module: New function convert_input_certificates
Certificates given by ansible could have leading and trailing white
space, but also multi line input is possible that also could have
leading and training white space and newlines.

New function:
- convert_input_certificates(module, certs, state)
2024-06-25 16:06:59 +02:00
Thomas Woerner
5ac7143f42 ipareplica: After an HSM replica install ensure all certs are visible
FreeIPA commit ea0bf4020ce0b1e32572e128e9323c5af60ec93d

    After an HSM replica install ensure all certs are visible

    If a certificate on a token does not have NSS trust set then
    it won't be visible in the softoken. This can be disconcerting
    for those used to seeing all the certificates.

    Loop through the possibilities and set no trust (or Peer) for
    all the certificates on the token.

    Also ensure that the CA certificate has the correct nickname.

    Related: https://pagure.io/freeipa/issue/9273
2024-06-20 15:21:36 +02:00
Thomas Woerner
07d91e02d1 ipareplica: Refactor CA file handling
replicainstall.install_ca_cert has been removed, paths.IPA_CERTUPDATE is
called instead if the client was configured before deploying with
iparepica role.

FreeIPA commit 8f25b2a74a587548976f3d29f0b69d566d70125d

    Refactor CA file handling in replica installer

    Clean up and remove obsolete code from ipa-replica-install. For several
    versions replica installer first ensures that a host is an IPA client,
    then promotes the client to a replica. The client installer code sets up
    CA stores like IPA_CA_CRT already.
2024-06-20 15:21:36 +02:00
Thomas Woerner
127d758100 ipareplica_install_ca_certs: Do not return unchanged config attributes
The config attributes config_master_host_name and also config_ca_host_name
are not changed within ipareplica_install_ca_certs, therefore it is not
needed to return them and also to use the returned values for following
tasks.
2024-06-20 15:21:36 +02:00
Thomas Woerner
4ff6e35c28 ipaserver: Set hsm attributes to None for now
The HSM parameters

    token_name
    token_library_path
    token_password
    token_password_file

are set to None to enable deployment with IPA 4.12 as a workaround till
HSM can be fully supported by the ipaserver role.
2024-06-20 15:21:36 +02:00
Rafael Guterres Jeffman
a1230cabc6 Merge pull request #1242 from t-woerner/fix_build_galaxy_release_sh_offline2
utils/build-galaxy-release.sh: Fix unary operator expected (v2)
2024-05-27 11:00:03 -03:00
Thomas Woerner
411f5f3467 utils/build-galaxy-release.sh: Fix unary operator expected (v2)
This fixes a bad tests if offline is not set:
utils/build-galaxy-release.sh: line 130: [: -ne: unary operator expected

Fixes f17f83d6bd
2024-05-27 15:39:35 +02:00
Thomas Woerner
8779384614 Merge pull request #1225 from rjeffman/ci_pin_ansible_lint_version
Bump linter tools versions an fix linter errors
2024-05-27 14:33:52 +02:00
Rafael Guterres Jeffman
2cc1484ad7 Merge pull request #1229 from t-woerner/batch_command
Use batch command internally
2024-05-23 14:53:23 -03:00
Rafael Guterres Jeffman
77c1d206d3 fixup! pylint: Ignore usage of 'unicode' before assignment 2024-05-22 14:31:00 -03:00
Rafael Guterres Jeffman
52241fe233 pylint: ensure variables are initialized
pylint doesn't know that some functions may terminate execution, like,
AnsibleModule's fail_json, and assume that, depending on the code path,
some variables may not be initialized when used.

This change ensure that variables are always initialized independent of
the code path.
2024-05-22 10:50:34 -03:00
Rafael Guterres Jeffman
f53ca3ad39 pylint: Ignore usage of 'unicode' before assignment
New versions of pylint ignore Python 2 functions and types, evaluating
'unicode' as "undefined". ansible-freeipa will always define 'unicode'
when running under Python 3, and it is always defined under Python 2.

This patch fixes these false positives.
2024-05-22 10:42:00 -03:00
Rafael Guterres Jeffman
60905ef5bf upstream ci: Update Github actions
Github actions checkout v3.1.0 and setup-python v4.3.0 use deprecated
Node.js 16.

Bumping version to checkout v4.1.1 and setup-python v5.1.0 fixes the
workflows, as both use the recommended Node.js 20.

The checkout depth has been set to 1 (shallow copy) for all tasks that
do not require git history to be available.
2024-05-22 10:40:49 -03:00
Rafael Guterres Jeffman
0d48da060d lint tools: bump code verification tools versions
Bump version of ansible-lint, Flake8, Pylint and yamllint to newer
versions as used in Ansible tests.
2024-05-22 10:40:49 -03:00
Thomas Woerner
5cdbcf6442 ipahost: Enable batch command with keeponly
The use of the batch command is enabled for execute_ipa_commands.

Additionally keeponly is set to ["randompassword"] as this is the only
parameter that is used from the data returned from the commands.
2024-05-22 11:51:22 +02:00
Thomas Woerner
08b0fc02ba ipagroup: Enable batch command use with keeponly
The use of the batch command is enabled for execute_ipa_commands.

Additionally keeponly is set to [] as nothing is used from the data returned
from the commands.
2024-05-22 11:50:11 +02:00
Thomas Woerner
6cec03eb15 ipaservice: Enable batch command use with keeponly
The use of the batch command is enabled for execute_ipa_commands.

Additionally keeponly is set to [] as nothing is used from the data returned
from the commands.
2024-05-22 11:48:36 +02:00
Thomas Woerner
65a1fd7804 ipauser: Enable batch command use with keeponly
The use of the batch command is enabled for execute_ipa_commands.

Additionally keeponly is set to ["randompassword"] as this is the only
parameter that is used from the data returned from the commands.
2024-05-22 11:45:03 +02:00
Thomas Woerner
bcb6a68230 IPAAnsibleModule: Add support for batch command in execute_ipa_commands
The method execute_ipa_commands has been extended to handle multi
commands with the batch command.

New constants for execute_ipa_commands debugging:

    DEBUG_COMMAND_ALL = 0b1111
    DEBUG_COMMAND_LIST = 0b0001
        Print the while command list
    DEBUG_COMMAND_COUNT = 0b0010
        Print the command number
    DEBUG_COMMAND_BATCH = 0b0100
        Print information about the batch slice size and currently executed
        batch slice

New parameters have been added to execute_ipa_commands:

    batch: bool
        Enable batch command use to speed up processing
    batch_slice_size: integer
        Maximum mumber of commands processed in a slice with the batch
        command
    keeponly: list of string
        The attributes to keep in the results returned.
        Default: None (Keep all)
    debug: integer
        Enable debug output for the exection using DEBUG_COMMAND_*

Batch mode can be enabled within the module with setting batch to True
for execute_ipa_commands.

Fixes: #1128 (batch command support)
2024-05-22 11:44:38 +02:00
Rafael Guterres Jeffman
8f8a16f815 Merge pull request #1239 from t-woerner/fix_group_readme_rename
README-group.md: Add missing ":" in multi rename example
2024-05-21 09:09:11 -03:00
Rafael Guterres Jeffman
bfcc62a27f Merge pull request #1238 from t-woerner/fix_user_readme_rename
README-user.md: Fix state for user rename in example playbook
2024-05-21 09:08:25 -03:00
Rafael Guterres Jeffman
8ba32bfc26 Merge pull request #1237 from t-woerner/fix_idempotency_issues_ipahost
ipahost: Fix idempotency issues
2024-05-21 09:06:45 -03:00
Thomas Woerner
69306a6177 README-group.md: Add missing ":" in multi rename example
The name tag in the multi rename example is was missing a ":".
2024-05-16 21:21:39 +02:00
Thomas Woerner
967a2d8e56 README-user.md: Fix state for user rename in example playbook
A user rename requires "state: renamed". This has been wrong in the
example.
2024-05-16 21:02:05 +02:00
Rafael Guterres Jeffman
2626715db6 Merge pull request #1222 from t-woerner/ipaserver_use_IPAChangeConf_and_realm_to_ldapi_uri
ipaserver_prepare: Properly create IPA_DEFAULT_CONF
2024-05-16 12:16:08 -03:00
Rafael Guterres Jeffman
2166a9f7a2 Merge pull request #1231 from t-woerner/inventory_plugin
New inventory plugin
2024-05-14 09:00:37 -03:00
Rafael Guterres Jeffman
8b4bb631a5 Merge pull request #1235 from t-woerner/fix_idempotency_issues_ipauser
ipauser: Fix idempotency issues for members
2024-05-14 08:55:10 -03:00
Thomas Woerner
f17f83d6bd utils/build-galaxy-release.sh: Fix unary operator expected
This fixes a bad tests if offline is not set:
utils/build-galaxy-release.sh: line 130: [: -ne: unary operator expected
2024-05-14 12:59:14 +02:00
Thomas Woerner
a3517a3a23 New inventory plugin
The inventory plugin compiles a dynamic inventory from IPA domain, filters
servers by role(s).

Usage:

Create yml file, for example `freeipa.yml`:

    ---
    plugin: freeipa
    server: server.ipa.local
    ipaadmin_password: SomeADMINpassword
    verify: ca.crt

Get compiled inventory:

    ansible-inventory -i freeipa.yml --graph
2024-05-14 12:58:39 +02:00
Rafael Guterres Jeffman
5aa1c7cb57 Merge pull request #1236 from t-woerner/fix_idempotency_issues_ipaservice
ipaservice: Do not set continue to None for service_del
2024-05-13 12:06:25 -03:00
Thomas Woerner
15e9201dab ipahost: Fix idempotency issues
This simplified the result_handler and also made the exception_handler
superfluous.
2024-05-13 13:31:52 +02:00
Thomas Woerner
6caa58e8be ansible_freeipa_module: Import and provide normalize_sshpubkey
normalize_sshpubkey is imported from ipalib.util and also added to
__all__ for use in modules.
2024-05-13 13:31:47 +02:00
Thomas Woerner
5c61f14cc1 ipaservice: Do not set continue to None for service_del
delete_continue defaults to None. The use of continue: None is resulting
in an error with the batch command. Therefore only set continue if it is
not None.
2024-05-13 13:23:26 +02:00
Thomas Woerner
b3a74e616a ipauser: Fix idempotency issues for members
These are manager, principal, certificate and certmapdata.

The result_handler function has been adapted and the exception_handler
function has been removed.

A new function has been added:

   convert_certificate
2024-05-13 13:18:26 +02:00
Rafael Guterres Jeffman
cbff802d13 Merge pull request #1226 from t-woerner/ipalib.install.kinit_moved_to_ipalib_freeipa_7286
ipalib.install.kinit moved to ipalib
2024-03-28 10:57:02 -03:00
Rafael Guterres Jeffman
4ceb6aa05d Merge pull request #1224 from t-woerner/ipaclient_dns_resolver_fix_changed
ipaclient_configure_dns_resolver: Return proper changed state
2024-03-28 10:13:06 -03:00
Thomas Woerner
35614d7a88 ipalib.install.kinit moved to ipalib
FreeIPA PR https://github.com/freeipa/freeipa/pull/7286 moved
ipalib.install.kinit to ipalib.

It is first tried to import kinit_keytab and kinit_password from
ipalib.kinit, then ipalib.install.kinit and finally in some cases
where support for IPA 4.5.0 is needed still also ipapython.ipautil.

Related: https://github.com/freeipa/freeipa/pull/7286
2024-03-27 15:33:29 +01:00
Rafael Guterres Jeffman
7a9ea832a1 Merge pull request #1227 from t-woerner/fix_build_galaxy_release_sh_offline
utils/build-galaxy-release.sh: Fix offline default value
2024-03-26 12:17:08 -03:00
Thomas Woerner
2804ec3f83 utils/build-galaxy-release.sh: Fix offline default value
The offline default value was 0, which resulted in 0 for
${offline/1/--offline}.

This broke the ansible-galaxy collection install call.
2024-03-26 14:46:11 +01:00
Thomas Woerner
bef748cfdc ipaclient_configure_dns_resolver: Return proper changed state
The changed state returned from ipaclient_configure_dns_resolver was
always True. The internal functions (copies from FreeIPA code) have been
fixed to return a changed state.

Fixes: #1217 (ipaclient: Configure DNS resolver always reports as changed)
2024-03-21 16:19:46 +01:00
Rafael Guterres Jeffman
c24e8b498e Merge pull request #1223 from t-woerner/galaxy_collection_for_rpm
utils/build-galaxy-release.sh: Enable offline generation for rpm
2024-03-21 09:27:41 -03:00
Thomas Woerner
fe16df8a6c utils/build-galaxy-release.sh: Enable offline generation for rpm
Two new options have been added to enable the offline build within rpm:

    -o <A.B.C>  Build offline without using git, using version A.B.C
                Also enables -a
    -p <path>   Installation the generated collection in the path, the
                ansible_collections sub directory will be created and will
                contain the collection: ansible_collections/<namespace>/<name>
                Also enables -i

The usage text has been fixed also for specifying namespace and name.
The collection variable has been renamed to name.

Example usage:

    utils/build-galaxy-release.sh -o 1.12.1 \
        -p %{buildroot}%{_datadir}/ansible/collections \
        freeipa ansible_freeipa
2024-03-20 13:45:07 +01:00
Rafael Guterres Jeffman
d804dc470e Merge pull request #1221 from t-woerner/ipaserver_only_one_custodia_setup
ipaserver: Run custodia setup only once
2024-03-14 10:44:41 -03:00
Thomas Woerner
8fa3daece8 ipaserver_prepare: Properly create IPA_DEFAULT_CONF
Use IPAChangeConf and realm_to_ldapi_uri to create IPA_DEFAULT_CONF.

With realm_to_ldapi_uri the ldap_uri is correctly using /run instead of
/var/run.

Before IPA_DEFAULT_CONF was created using file operations.
2024-03-13 14:27:09 +01:00
Thomas Woerner
0cad1fa879 ipaserver: Run custodia setup only once
The custodia setup is executed twice. At first in
ipaserver_setup_custodia and then additionally in ipaserver_setup_ca.

The custodia setup code in ipaserver_setup_ca.py has been adapted to fit
the code in ipaserver_setup_custodia.py.

The extra Setup custodia step in the server roles has been removed
together with ipaserver_setup_custodia.py.
2024-03-13 13:15:24 +01:00
Rafael Guterres Jeffman
780e6b1436 Merge pull request #1220 from t-woerner/ipaserver_test_return_generated_domain_name
ipaserver_test: Return generated domain_name
2024-03-11 11:08:39 -03:00
Rafael Guterres Jeffman
216a5d4f9d Merge pull request #1215 from t-woerner/fix_ca_less_to_use_X.509_v3
Fix ca-less test to use X.509 v3 certificates
2024-03-11 11:04:08 -03:00
Thomas Woerner
f8ff833b03 ipaserver_test: Return generated domain_name
If ipaserver_domain is not given, the domain name is generated from the
host fqdn.

This generated value was so far not returned, but the empty given value
instead.
2024-03-11 14:02:51 +01:00
Thomas Woerner
b92da82661 Fix ca-less test to use X.509 v3 certificates
The generated certificates have been X.509 v1. This is not supported any
more. Only X.509 v3 is supported.

A new certificates/extensions.conf file has been added to make v3
certificates.

The existing certificates/pkinit/extensions.conf has been renamed to
certificates/pkinit-extensions.conf with additional changes. For example
"[kdc_cert]" had to be removed for v3.

The extensions config files are using environment variables, which are
set by the generate-certificates.sh script before calling openssl.

The script generate-certificates.sh has been reworked for a simpler
structure, also new options have been added: "ca" and "cleanup".
2024-03-05 11:17:17 +01:00
Thomas Woerner
ce05b5e137 Merge pull request #1213 from rjeffman/dnszone_fix_yaml_code_block
README-dnszone: Fix yaml code block declaration.
2024-02-27 13:10:19 +01:00
Rafael Guterres Jeffman
a826bf1781 README-dnszone: Fix yaml code block declaration.
There was a space between the code block marker and the highlight hint
in a playbook example.
2024-02-15 09:39:14 -03:00
Thomas Woerner
a3a6919416 Merge pull request #760 from rjeffman/ipadelegation_case_insensitive
ipadelegation: Fix idempotence issues due to capitalization.
2024-02-12 15:33:30 +01:00
Rafael Guterres Jeffman
e9c6e93608 ipadelegation: Fix idempotence issues due to capitalization.
This patch force processing of permission, attribute and group
attributes in lower case, to match behavior of IPA CLI, transforming
all of them into lowercase characters.

The new behavior fixes idempotence issues when mixing different
capitalization in different tasks for the same attribute.

A new test playbook is avaiable at:

    tests/delegation/test_delegation_member_case_insensitive.yml
2024-02-12 11:10:21 -03:00
Thomas Woerner
f40f4d4c9a Merge pull request #1201 from rjeffman/ipagroup_case_insensitive
ipagroup: Fix idempotence issues due to capitalization
2024-02-12 14:59:41 +01:00
Rafael Guterres Jeffman
7b7d9c9957 ipagroup: Fix idempotence issues due to capitalization
Some attributes for ipagroup objects are stored using lower case letters
and should be converted upon retrieving parameter data.

This patch adds the missing conversion and provides a new test playbook:

    tests/group/test_group_case_insensitive.yml
2024-02-12 09:11:12 -03:00
Rafael Guterres Jeffman
c0c3394d8d Merge pull request #1211 from t-woerner/disable_config_tests_for_pac_type_without_MS-PAC
Disable config tests for pac type without ms pac
2024-02-12 09:09:06 -03:00
Thomas Woerner
11205102af Merge pull request #1202 from rjeffman/ipahostgroup_idempotence_issues
ipahostgroup: Fix idempotence issues due to capitalization
2024-02-12 11:45:05 +01:00
Rafael Guterres Jeffman
22401d18d6 ipahostgroup: Fix idempotence issues due to capitalization
ipahostgroup parameters 'host', 'hostgroup', 'membermanager_user' and
'membermanager_group' must be compared in a case insensitive manner
and stored as lower case strings.

This patch fixes the comparison and storage of this parameters, and
change the handling of members to use the same structure as in newer
modules.

Two new tests files were added:

    tests/hostgroup/test_hostgroup_case_insensitive.yml
    tests/hostgroup/test_hostgroup_membermanager_case_insensitive.yml
2024-02-09 21:19:58 -03:00
Thomas Woerner
9b5a54c4fa Merge pull request #1203 from rjeffman/ipahbacrule_fix_idempotence_issues
ipahbacrule: Fix handling of hbacsvcgroup in members
2024-02-09 19:49:28 +01:00
Thomas Woerner
9920a76777 config: Disable config tests due to pac type requirement MS-PAC
The config tests are currently setting the pac type to empty or without
MS-PAC type. This results in failed authorization for IPA API.

An issue has been opened for FreeIPA to address this:
https://pagure.io/freeipa/issue/9527
2024-02-09 14:43:46 +01:00
Rafael Guterres Jeffman
249eab6047 Merge pull request #1208 from t-woerner/ipaclient_automount_with_new_install_states
ipaclient_setup_automount with new install states
2024-02-07 13:10:24 -03:00
Thomas Woerner
29f046b8e2 Merge pull request #1206 from rjeffman/ipaserver_fix_deploy_EL8
ipaserver: Fix deployment after Bronze-bit fix
2024-02-07 16:54:11 +01:00
Rafael Guterres Jeffman
2317c20556 ipaserver: Fix deployment after Bronze-bit fix
As FreeIPA now requires MS-PAC to be set in ipaKrbAuthzData to trigger
PAC generation, there's a timing issue that causes API malfunction which
is long enough to cause the client part insallation to fail.

By restarting KDC after DS password is set, we force cached values to be
refreshed, allowing the API to work correctly.

Resolves: https://github.com/freeipa/ansible-freeipa/issues/1200
2024-02-07 12:16:38 -03:00
Thomas Woerner
0d1f8b53b8 ipaclient_setup_automount: Only return changed if there was a change
The returned changed state was always True. changed is now only True if
automount_location is set and configure_automount was called.
2024-02-07 14:39:34 +01:00
Thomas Woerner
0a468d32e8 ipaclient_setup_automount with new install states
This is "Fix ipa-client-automount install/uninstall with new install
states" https://github.com/freeipa/freeipa/pull/7100 for ansible-freeipa:

Issue 8384 introduced a new installation state for the statestore
to identify when client/server installation is completely finished
rather than relying on has_files().

The problem is that ipa-client-automount may be called during
ipa-client-install and since installation is not complete at that
point the automount install was failing with "IPA client not
configured".

Add a new state, 'automount', to designate that automount installation
is in process. If check_client_configuration() fails it checks to
see if [installation] automount is True. If so it continues with the
installation.

This also addresses an issue where the filestore and statestore are
shared between the client and automount installers but the client
wasn't refreshing state after automount completed. This resulted in
an incomplete state and index file of backed-up files which caused
files to not be restored on uninstall and the state file to be
orphaned.

Fixes: https://pagure.io/freeipa/issue/9487
2024-02-07 14:39:04 +01:00
Rafael Guterres Jeffman
03c65bd761 Merge pull request #1207 from t-woerner/ipaclient_enable_SELinux_for_SSSD
ipaclient: Enable SELinux for SSSD
2024-02-07 00:38:13 -03:00
Rafael Guterres Jeffman
b87b346a0a ipahbacrule: Fix handling of hbacsvcgroup in members
FreeIPA provides a default hbacsvcgroup named "Sudo", with capital 'S',
that is different from every other hbacsvcgroup, which are all
represented by lower case letters.

As data from IPA API was not modified, this causes an idempotence error
when using different capitalization with the 'hbacsvcgroup' parameter.

This patch fixes the issue by using the CaseInsensitive comparator to
create the hbacsvcgroup list.

Tests were update to make sure a regression is not included in the
future.
2024-02-06 16:29:04 -03:00
Thomas Woerner
e92db5c5cd ipaclient: Enable SELinux for SSSD
This is "ipa-client-install: enable SELinux for SSSD"
https://github.com/freeipa/freeipa/pull/6978 for ansible-freeipa:

For passkeys (FIDO2) support, SSSD uses libfido2 library which needs
access to USB devices. Add SELinux booleans handling to ipa-client-install
so that correct SELinux booleans can be enabled and disabled during
install and uninstall. Ignore and record a warning when SELinux policy
does not support the boolean.

Fixes: https://pagure.io/freeipa/issue/9434
2024-02-06 14:39:19 +01:00
Thomas Woerner
1028f61b6c Merge pull request #899 from rjeffman/sudorule_add_runasuser_group
ipasudorule: Allow setting groups for runasuser.
2024-01-24 22:11:43 +01:00
Rafael Guterres Jeffman
1fde1764af ipasudorule: Allow setting groups for runasuser.
On IPA CLI sudorule-add/del-runasuser accept 'group' as a parameter,
and this option was missing in ansible-freeipa ipasudorule module.

This patch adds a new parameter 'runasuser_group' to allow setting
Groups of RunAs Users, as allowed by CLI and WebUI.

New example playboks can be found at:

    playbooks/sudorule/ensure-sudorule-runasusesr-group-is-absent.yml
    playbooks/sudorule/ensure-sudorule-runasusesr-group-is-present.yml
2024-01-23 12:04:02 -03:00
Thomas Woerner
4321478cf0 Merge pull request #1178 from rjeffman/ipagroup_rename
ipagroup: Add support for renaming groups
2023-12-21 20:47:47 +01:00
Rafael Guterres Jeffman
900c76e810 Merge pull request #1195 from t-woerner/Fixes_for_ansible_lint_6_22_1
Fixes for ansible-lint 6.22.1
2023-12-20 16:10:46 -03:00
Rafael Guterres Jeffman
1ecdbd3a49 ipagroup: Add support for renaming groups
FreeIPA suports renaming groupobjects with the CLI parameter "rename",
and this parameter was missing in ansible-freeipa ipagroup module.

This patch adds support for a new state 'renamed' and the 'rename'
parameter.

Tests were updated to cope with the changes.
2023-12-20 11:29:22 -03:00
Thomas Woerner
47a1d50c84 Fixes for ansible-lint 6.22.1
- Replace outdated noqa 503 with noqa no-handler
- Drop outdated and not needed noqa 505 for include_vars
- Drop outdated noqa deprecated-command-syntax for
  ansible.builtin.shell using cmd tag

These warnings have been reported by utils/lint_check.sh using
ansible-lint 6.22.1.
2023-12-20 14:38:24 +01:00
Rafael Guterres Jeffman
3fe41a5260 tests/group: Use module_defaults on tests_group
Use module_defaults to improve reading test cases.
2023-12-20 09:21:17 -03:00
Thomas Woerner
3a304e8bd7 Merge pull request #1174 from rjeffman/ipauser_rename
ipauser: Add support for renaming users
2023-12-20 11:40:50 +01:00
Thomas Woerner
86e089fd42 Merge pull request #1147 from rjeffman/dnszone_permission
ipadnszone: Add support for per-zone privilege delegation
2023-12-20 11:21:35 +01:00
Rafael Guterres Jeffman
3eb86b2c2d ipauser: Add support for renaming users
FreeIPA suports renaming user objects with the CLI parameter "rename",
and this parameter was missing in ansible-freeipa ipauser module.

This patch adds support for a new state 'renamed' and the 'rename'
parameter.

Tests were updated to cope with the changes.

Related to RHBZ#2234379, RHBZ#2234380

Fixes #1103
2023-12-19 11:44:31 -03:00
Rafael Guterres Jeffman
3bd68ac0fa ipadnszone: Add support for per-zone privilege delegation
IPA DNS Zones management can be delegated by adding a "Manage DNS zone"
permission. The CLI commands that manage these permissions are
dnszone-add-permission and dnszone-remove-permission.

The ansible-freeipa module ipadnszone did not have this capability, and
it now support dnszone per-zone management delegation by setting the
module parameter 'permission'. If set to 'true' the permission will be
assigned to the zone, if set to false the permission will be removed.
2023-12-19 11:28:46 -03:00
Rafael Guterres Jeffman
0f2c37612e Merge pull request #1169 from t-woerner/ipaclient_automount_location
ipaclient: Properly name automount_location var and add documentation
2023-12-19 09:46:41 -03:00
Thomas Woerner
4e831b0cb8 Merge pull request #1143 from rjeffman/global_handle_datatype
Handle data type or empty string in module_utils
2023-12-19 13:35:04 +01:00
Rafael Guterres Jeffman
34973c04c6 idoveridegroup: Use module.params_get_type
Use the commom parameter type handling method for parameters that accept
a value or an empty string.
2023-12-15 10:48:00 -03:00
Rafael Guterres Jeffman
bc694b722c idoverideuser: Use module.params_get_type
Use the commom parameter type handling method for parameters that accept
a value or an empty string.
2023-12-15 10:48:00 -03:00
Rafael Guterres Jeffman
92d579be41 ipapwpolicy: Use modules.params_get_type
Use the commom parameter type handling method for parameters that accept
a value or an empty string.
2023-12-15 10:48:00 -03:00
Rafael Guterres Jeffman
e55a41ca0c ansible_freeipa_module: Ensure data type when retrieving parameter
Some parameters, in modules, have a specific data type, but allow the
use of an empty string to clear the parameter.

By providing a method to retrieve the parameter with the correct data
type, or optionally an empty string, allows for consistency of parameter
handling between different modules.
2023-12-15 10:41:41 -03:00
Thomas Woerner
0f7ebd22fd Merge pull request #1149 from rjeffman/fix_rhel_4934
ipauser: Do not try to modify user when not changing password
2023-12-14 14:56:21 +01:00
Rafael Guterres Jeffman
f4c9e28715 Rename parameter 'allow_empty_string' to 'allow_empty_list_item'
The parameter 'allow_empty_string' in 'module_params_get' is used to
allow an item in a list to be an empty string. The problem is that the
naming is misleading, as it is checking a list item rather than a
string.

This patch rename the parameter to 'allow_empty_list_item' so that it
more clearly refers to list itens instead of standalone strings, and do
not collide with future parameters that may test for empty strings which
are not part of lists.
2023-12-08 14:12:52 -03:00
Thomas Woerner
81e6cbe6b7 Merge pull request #1187 from rjeffman/ipaclient_fix_otp_error_report
ipaclient: Fix OTP error reporting
2023-12-08 17:17:13 +01:00
Rafael Guterres Jeffman
9ecbe2315e Merge pull request #1189 from t-woerner/revert_temp_commit_de3c6c0
Revert "[TEMP] Enable only idp, service and user module tests"
2023-12-07 11:41:32 -03:00
Thomas Woerner
102d6c5a6d Revert "[TEMP] Enable only idp, service and user module tests"
This reverts commit de3c6c0ace.
2023-12-07 15:31:08 +01:00
Thomas Woerner
66bbc50c4d Merge pull request #1151 from rjeffman/ipareplica_support_cluster_ipaserver
ipareplica: Support inventory groups.ipaserver
2023-12-07 14:30:28 +01:00
Thomas Woerner
a38106afae Merge pull request #1184 from rjeffman/ci_inscrease_pr_test_timeout
upstream ci: Increase timeout for PR tests
2023-12-07 14:05:41 +01:00
Rafael Guterres Jeffman
47940b48c6 upstream ci: Increase timeout for PR tests
After the change for a single job to run PR tests, and if there is any
change to ansible_module_utils, all the playbook tests are executed,
and the result is a failure due to timeout.

This PR increases the timeout so that a PR with changes to
ansible_module_utils can have the tests executed.
2023-12-07 09:11:20 -03:00
Rafael Guterres Jeffman
8114120814 Merge pull request #1183 from t-woerner/idp_fix_validation_and_reset
ipaidp: Fix validation and reset of parameters
2023-12-07 09:10:26 -03:00
Thomas Woerner
505cb356c1 Merge pull request #1188 from rjeffman/ci_bump_ansible_lint
Bump minimum ansible-lint version to 6.22
2023-12-07 10:11:21 +01:00
Rafael Guterres Jeffman
d2e0cad90b Bump minimum ansible-lint version to 6.22
By the first quarter of 2024, all collections must pass ansible-lint
tests run with version 6.22.x. This PR ensure that all ansible-freeipa
tests depending on ansible-lint use a valid version of it.
2023-12-06 15:35:17 -03:00
Rafael Guterres Jeffman
9c735939a2 Merge pull request #1186 from t-woerner/test_host_random_conditional_statements_no_jinja2_templating
test_host_random: No jinja2 templating in conditional statements
2023-12-06 15:26:38 -03:00
Rafael Guterres Jeffman
22214dafff ipaclient: Fix OTP error reporting
When deploying an IPA client with ipaclient, if an error occured while
getting an OTP, no error message is logged, as the task that logs the
error is not excuted due to the previous taks failure.

By adding a 'rescue' section to the code block and moving the error
reporting to this new section, we ensure that the proper error messages
will be reported.
2023-12-06 14:26:00 -03:00
Thomas Woerner
2c9ee7d842 test_host_random: No jinja2 templating in conditional statements
With ansible-core 2.14.12 using jinja2 templating in conditional
statements with ansible_facts['fqdn'] is marked as unsafe and results in
a failure.

The issues with using jinja2 templating in conditional statements for
asserts have been solved and a new server_fqdn fact has been added for
ansible_facts['fqdn'].
2023-12-06 13:23:47 +01:00
Thomas Woerner
de3c6c0ace [TEMP] Enable only idp, service and user module tests 2023-12-05 16:44:55 +01:00
Thomas Woerner
ff084fbd96 ipaidp: Fix validation and reset of parameters
The uri parameters auth_uri, dev_auth_uri, token_uri, userinfo_uri and
keys_uri have not been validated before. Also the base_url was not
normalized. The auth_uri, dev_auth_uri, token_uri and userinfo_uri need
to be set for new entries, but might be empty or empty string for reset
or updates.

The ipaidpclientsecret needs to be decoded from binary string in
find_idp result to not trigger no change ipd_mod calls.

The code for validate_uri and base_url normalization has been copied
from the ipaserver idp plugin.

ansible_freeipa_module:
urlparse from urllib.parse with a fallback to six.moves.urllib.parse is
imported and also exported. urlparse is needed for validate_uri in ipaidp
module.

Resolves: RHEL-17954, RHEL-17955, RHEL-17957 and RHEL-17958
2023-12-05 16:30:50 +01:00
Rafael Guterres Jeffman
ca5496918a ipauser: Do not try to modify user when not changing password
If a playbook to ensure the existence of a user contains 'random:false'
and 'update_password: always' is executed twice, the second execution
will raise an exception due to "No modifications to perform", as there
is actually nothing to modify.

The fix for the issue is to remove 'random' if it is not set to true, as
setting it to 'false' would have no effect on the user object.

Related: https://issues.redhat.com/browse/RHEL-4934
2023-11-28 16:03:27 -03:00
Rafael Guterres Jeffman
48c0fd0a28 Merge pull request #1180 from t-woerner/freeipa_9297_pwpolicy_minlength_reset
test_pwpolicy: minlength parameter can be reset with empty string now
2023-11-28 08:59:50 -03:00
Thomas Woerner
f2a1d50b82 test_pwpolicy: minlength parameter can be reset with empty string now
The reset of the minlength parameter failed with an internal error so
far. This has been fixed in IPA and therefore requires to fix the test
in ansible-freeipa also.

Related: https://pagure.io/freeipa/issue/9297
2023-11-28 11:48:31 +01:00
Thomas Woerner
1930d8c8be Merge pull request #1168 from rjeffman/dev_reproduce_upstream_groups
Reproduce upstream CI groups in developer's machine
2023-11-24 15:39:47 +01:00
Thomas Woerner
1837ee662c Merge pull request #1104 from rjeffman/ci_parallel_image_build
upstream CI: Build containers in parallel jobs
2023-11-24 14:50:44 +01:00
Thomas Woerner
26e171df79 Merge pull request #1170 from rjeffman/ci_ansible_future
Update ansible-lint and pylint versions
2023-11-24 14:46:22 +01:00
Thomas Woerner
01440e3c04 Merge pull request #1173 from rjeffman/ipahost_fix_dnsrecords
ipahost: Remove dangling dns records during test setup
2023-11-24 14:44:19 +01:00
Rafael Guterres Jeffman
2426e04c22 Merge pull request #1176 from t-woerner/idoverrideX_del_without_delete_continue
idoverride{user,group}: Fix delete_continue with state absent
2023-11-24 09:02:05 -03:00
Thomas Woerner
92e44f6a6c idoverride{user,group}: Fix delete_continue with state absent
All tasks for idoverrideuser and idoverridegroup with state absent
failed with "'continue' is required" when delete_continue was not set.

This happended as delete_continue was internally None and continue: None
was provided to the API.

The fix is simply to use '"continue": delete_continue or False' so that
continue is set to False in this case.
2023-11-16 13:47:04 +01:00
Rafael Guterres Jeffman
16c8ee87e9 ipahost: Remove dangling dns records during test setup
When testing ipahost through the test playbooks, if there are previous
DNS A/AAAA records, the test fails due to a false positive idempotence
issue.

This patch ensures that all DNS records for the test hosts are absent
before test execution.

This issue could be seen in the 2023-11-06 Azure Nightly pipeline
execution.
2023-11-10 20:26:51 -03:00
Rafael Guterres Jeffman
3109e9d1bc utils/run-tests.sh: Replicate Azure's test grouping
When running ansible-freeipa's Azure pipelines for nightly and weekly
tests, due to the amount of tests to execute, tests are grouped and
executed in parallel jobs.

Due to a still unkonwn issue, depending on the order the tests are
executed, some random failures may occur and debugging them is hard due
to current implementation of the tests.

This patch adds support for replicating the tests of a specific Azure
test group once the seed used to create groups and the group number are
provided, allowing the test failures to be replicated on the developer's
workstation where it can be more easily debugged.

A new option is added to 'utils/run-tests.sh', '-A SEED.G' that is used
to define the seed and group to replicate the tests. The seed is a date,
with the format "YYYYMMDD", so, for example '-A 20230611.2' would
execute the same tests, in the same order as the second group of tests
for date 2023-06-11. To aid in usability 'YYYY-MM-DD' may also be used.

When using '-A' neither '-s' (test suites) or specific tests (positional
arguments) can be used.

Also, to help fixing tests, an option to stop the tests on the first
test failure ('-x') was added to the script.
2023-11-09 12:36:59 -03:00
Rafael Guterres Jeffman
b457de545d Update ansible-lint and pylint versions
ansible-lint 6.21+ and pylint 3.0+ will be required for Ansible
collections to be approved on Ansible Galaxy.

This patch updates pre-commit and upstream linters to use the required
versions.
2023-11-08 15:11:02 -03:00
Thomas Woerner
b22bf4dfb9 ipaclient: Properly name automount_location var and add documentation
The ipaclient_automount_location variable was badly named as
ipaautomount_location. Additionally it was not documented in the role
README file.

Fixes: #1166 (.. automount-location to the ipa-client role)
2023-11-08 12:33:41 +01:00
Rafael Guterres Jeffman
f1a6f44477 Merge pull request #1158 from t-woerner/idview_fail_to_apply_invalid_hosts
ipaidview: Fail to apply unknown (invalid) hosts
2023-10-22 22:43:03 -03:00
Rafael Guterres Jeffman
1dbe19cefb Merge pull request #1156 from t-woerner/hbacsvcgroup_remove_oobsolete_result_handler
hbacsvcgroup: Remove obsolete result_handler
2023-10-22 22:42:06 -03:00
Rafael Guterres Jeffman
7982fad342 Merge pull request #1155 from t-woerner/hbacrule_with_svcgroup_Sudo
hbacrule: Fix use of builtin sudo hbacsvcgroup
2023-10-22 22:41:18 -03:00
Thomas Woerner
212719496c ipaidview: Fail to apply unknown (invalid) hosts
The task to apply an unknown (invalid) host to an idview was not failing
as expected and only reported no change.

A new host verification step has been added to fail before trying to
apply invalid hosts. unapplying an invalid host is not failing as the
invalid host is indeed not applied.
2023-10-21 01:10:13 +02:00
Thomas Woerner
3de6f9146e hbacsvcgroup: Remove obsolete result_handler
The result_handler is not needed anymore as the idempotency issues with
members have been fixed already for this module.

Related: #685 hbacsvcgroup: Fix member management idempotence issues.
2023-10-21 01:09:21 +02:00
Thomas Woerner
48f2ef88a4 hbacrule: Fix use of builtin sudo hbacsvcgroup
hbacsvcgroup names are converted to lower case while creation with
hbacsvcgroup_add.

The hbacsvcgroup for sudo is builtin with the name "Sudo" though. This
breaks the lower case comparison. Therefore all memberservice_hbacsvcgroup
items are converted to lower case if "Sudo" is in the list.
2023-10-21 01:08:44 +02:00
Rafael Guterres Jeffman
6845acd596 upstream CI: Build containers in parallel jobs
In the current build container pipeline, all steps are serialized in a
single job, and if one of the jobs fail to build, due to broken
dependent image, or some Azure glitch, like slow connection, the only
way to rebuild the failed container is to rebuild all containers.

By building containers in parallel jobs, if a container fails to build
it is possible to restart only the failed job.
2023-10-20 13:44:09 -03:00
Rafael Guterres Jeffman
f012da22ce ipareplica: Support inventory groups.ipaserver
Altough most of ansible-freeipa documentation and playbooks use
'ipaserver' as the group for the first server deployed for a realm, the
ipareplica role only supported the use of groups["ipaservers"] as an
alternative to set ipareplica_servers.

Also supporting groups.ipaserver, as already supported by the ipaclient
role, make ansible-freeipa playbooks more consistent and current
documentation and examples easier to follow when deploying a cluster
with a server and a replica.
2023-10-20 13:43:06 -03:00
Thomas Woerner
ba7bf0f6cd Merge pull request #1148 from rjeffman/fix_checkpr_test_selection
upstream CI: Fix test selection for CheckPR pipeline.
2023-10-20 16:35:03 +02:00
Rafael Guterres Jeffman
fe2d17e4df upstream ci: Run PR tests using a single job.
The usual scenario for PR checks is to execute only a few tests, and
searching for the results in several jobs makes it harder to find
issues.

By using a single job run the tests would take some more time to
complete, although not much, as only a small subset is executed, and
test verification would be easier and less error prone.
2023-10-19 09:41:55 -03:00
Rafael Guterres Jeffman
319a0d3d86 upstream ci: Use a single random seed for spliting tests
Dependind on how long it took for the jobs to start, a different seed
would be used to group tests and tests could either repeat or not be
selected at all.

By using a seed based on the day the test run reduces the chance of
using different random seeds, and still allow for the tests to be
executed in a different order.

The execution in different order is important to identify tests that
work or fail only if executed after other tests.
2023-10-19 09:41:55 -03:00
Rafael Guterres Jeffman
c71a2b33dd upstream CI: Fix test selection for CheckPR pipeline.
Due to an error on processing Ansible key 'import_tasks' the script that
creates a list of modules to test is broken making some modules to be
not tested.

By fixing the handling of 'import_tasks' and module import, the list is
correct again and the list of modules to be tested now include the ones
which depend on the modified module.
2023-10-19 09:41:55 -03:00
Thomas Woerner
02223dfb67 Merge pull request #1159 from rjeffman/ci_pin_ansible_lint
upstream CI: Pin ansible-lint version to 6.20 series
2023-10-19 13:18:27 +02:00
Rafael Guterres Jeffman
5731a1539b upstream CI: Pin ansible-lint version to 6.20 series
The release version 6.21.0 of ansible-lint introduced a bug that breaks
the reporting of 'warning' messages. [1]

This patch pins ansible-lint version to the latest one in the 6.20
series, so that it can still be used to check pull requests.

[1]: https://github.com/ansible/ansible-lint/issues/3853
2023-10-18 15:36:16 -03:00
Thomas Woerner
ee7354230b Merge pull request #1157 from rjeffman/ci_fix_docker_install
upstream CI: Pin Python version to 3.11
2023-10-18 16:14:49 +02:00
Rafael Guterres Jeffman
4bb40f3397 Merge pull request #1146 from t-woerner/update_ansible-freeipa.spec.in_for_doverridegroup
utils/ansible-freeipa.spec.in: Add ref for idoverridegroup management
2023-10-17 17:05:39 -03:00
Rafael Guterres Jeffman
55b8729c52 upstream CI: Pin Python version to 3.11
Azure Ubuntu images have Python 3.12 available, and as we did not pin
the requested Python version, the latest available one was used, causing
image preparation and tests to fail.

This patch pins Python version to 3.11 until test can be executed with
Python 3.12 and later.
2023-10-17 16:32:37 -03:00
Rafael Guterres Jeffman
539ace413d Merge pull request #1105 from t-woerner/new_idp_module
New idp management module
2023-09-27 12:12:14 -03:00
Thomas Woerner
0c20b34d28 utils/ansible-freeipa.spec.in: Add ref for idoverridegroup management
The idoverridegroup management reference has been added to the
description.
2023-09-27 14:43:11 +02:00
Thomas Woerner
f9ff41320f New idp management module
There is a new idp management module placed in the plugins folder:

    plugins/modules/ipaidp.py

The idp module allows to ensure presence or absence of external Identity
Providers.

Here is the documentation for the module:

    README-idp.md

New idp example playbooks:

    playbooks/idp/idp-present.yml
    playbooks/idp/idp-absent.yml

New tests for the module:

    tests/idp/test_idp.yml
    tests/idp/test_idp_client_context.yml
2023-09-27 10:52:55 +02:00
Thomas Woerner
69c6b4d644 Merge pull request #1145 from rjeffman/revert_ansible_2_9
Revert "upstream ci: Run nightly tests against Ansible 2.9"
2023-09-26 17:41:08 +02:00
Rafael Guterres Jeffman
b63716b724 Revert "upstream ci: Run nightly tests against Ansible 2.9"
Most of our usptream CI test imagens do not handle Ansible 2.9 so, this
cange is being reverted.

This reverts commit 34654d1090.
2023-09-26 11:25:58 -03:00
Thomas Woerner
3cf138674b Merge pull request #1144 from rjeffman/upstream_ci_ansible_2_9
Ensure CI runs against the oldest supported Ansible versions.
2023-09-25 16:04:59 +02:00
Thomas Woerner
12e0d110f6 Merge pull request #1112 from rjeffman/future_pylint
Bump linter versions.
2023-09-25 16:02:53 +02:00
Rafael Guterres Jeffman
34654d1090 upstream ci: Run nightly tests against Ansible 2.9
Recently it was announced that Ansible 2.9 will be supported for some
time, and this patch ensures that we run the nightly tests against this
version of Ansible.
2023-09-19 15:54:38 -03:00
Rafael Guterres Jeffman
72d3ab8e04 upstream ci: Run PR checks against the oldest supported ansible-core
Recently, a change in the deployment roles forced the change to the
minimum version of ansible-core, and the change was unnoticed until
reported.

With this patch, we ensure all PRs checks are executed against the
minimun supported ansible-core version, so we can ensure that both
documentation and role metadata are correct and still valid.
2023-09-19 15:54:38 -03:00
Rafael Guterres Jeffman
fb75aed663 Merge pull request #1141 from t-woerner/new_idoverridegroup_module
New idoverridegroup management module.
2023-09-18 11:05:30 -03:00
Thomas Woerner
6f5bb9eebf New idoverridegroup management module.
There is a new idoverridegroup management module placed in the plugins
folder:

    plugins/modules/ipaidoverridegroup.py

The idoverridegroup module allows to ensure presence and absence of
idoverrides for groups.

Here is the documentation for the module:

    README-idoverridegroup.md

New example playbooks have been added:

    playbooks/idoverridegroup/idoverridegroup-absent.yml
    playbooks/idoverridegroup/idoverridegroup-present.yml

New tests for the module can be found at:

    tests/idoverridegroup/test_idoverridegroup.yml
    tests/idoverridegroup/test_idoverridegroup_client_context.yml
2023-09-18 15:17:08 +02:00
Rafael Guterres Jeffman
e5b2c122ce Merge pull request #1139 from t-woerner/new_idoverrideuer_module
New idoverrideuser management module.
2023-09-16 09:03:48 -03:00
Thomas Woerner
c0692e1746 New idoverrideuser management module.
There is a new idoverrideuser management module placed in the plugins
folder:

    plugins/modules/ipaidoverrideuser.py

The idoverrideuser module allows to ensure presence and absence of
idoverrides for users and certificate members.

Here is the documentation for the module:

    README-idoverrideuser.md

New example playbooks have been added:

    playbooks/idoverrideuser/idoverrideuser-absent.yml
    playbooks/idoverrideuser/idoverrideuser-certificate-absent.yml
    playbooks/idoverrideuser/idoverrideuser-certificate-present.yml
    playbooks/idoverrideuser/idoverrideuser-present.yml

New tests for the module can be found at:

    tests/idoverrideuser/test_idoverrideuser.yml
    tests/idoverrideuser/test_idoverrideuser_client_context.yml
2023-09-15 18:35:21 +02:00
Rafael Guterres Jeffman
2d079c8eec Merge pull request #1142 from t-woerner/Do_not_use_del_os.environ
Do not use "del os.environ" as the variable might not exist
2023-09-14 11:19:03 -03:00
Thomas Woerner
b70a1ecf61 Do not use "del os.environ" as the variable might not exist
The use of del os.environ assumes that the environment variable exists.
If the variable does not exist, this call will result in a traceback.
The solution is to use os.environ.pop(VARIABLE, None) instead.

This is the ansible-freeipa fix for https://pagure.io/freeipa/issue/9446
(Nightly test failure for replica installation with --setup-ca)
2023-09-14 15:20:37 +02:00
Rafael Guterres Jeffman
7cb5e481e5 Merge pull request #1140 from t-woerner/new_module_template_fixes
new_module template fixes
2023-09-13 11:07:02 -03:00
Thomas Woerner
60593b7dd3 utils/templates/ipamodule*.py.in: Fix superfluous type in argument spec
The type was given twice for state and action argument specs. This has
been fixed.
2023-09-13 13:20:47 +02:00
Thomas Woerner
e84ed3b6ba utils/templates/test_module_client_context.yml.in: Fix FQDN issue
Fixes left over FQDN issue for include_tasks.
2023-09-13 13:09:24 +02:00
Rafael Guterres Jeffman
6e1f9f1a72 pylint: Fix redefined-builtin 2023-09-11 12:01:17 -03:00
Rafael Guterres Jeffman
46a307aaeb pylint: Fix unused-argument 2023-09-11 12:01:17 -03:00
Rafael Guterres Jeffman
d8f8211a1c ci: Bump pylint version
Change pylint version to match latest version on Fedora 38.
2023-09-11 12:01:17 -03:00
Rafael Guterres Jeffman
34daa992f5 development: Bump versions of development checks
Update versions for linters and pre-commit checks, and fix ansible-lint
execution.
2023-09-11 12:01:17 -03:00
Rafael Guterres Jeffman
07c1a5ee61 pylint: Unnecessary parens after '=' keyword
This patch removes unnecessary usage of parens on attributions.
2023-09-11 12:01:17 -03:00
Rafael Guterres Jeffman
63d0272385 Change 'Exception' to 'RuntimeError' when FreeIPA version is too old
Changing the use of 'Exception' to 'RuntimeError' has the benefits of
making the error more specific and meaningful for what is being reported
and to remove warnings from linters (pylint).

The same change is applied to all deployment roles.
2023-09-11 12:01:17 -03:00
Rafael Guterres Jeffman
d0a8005a7f pylint: Disable broad exception warnings
In recent pylint versions, use of broad exceptions for both raise and
try/except blocks raise a linter warning. As its use is justifiable in
the case of ipavault, the warnings are disabled where they occur.
2023-09-11 12:01:17 -03:00
Rafael Guterres Jeffman
24efad73fa pylint: Fix warning 'unnecessary "else" after "return"'
Recent pylint versions warn against the use of an 'else' in a
'try-except' block if using a 'return' on the 'except' part is is the
idom used by ansible-freeipa when retrieving IPA data objects.

This change removes the usage of the 'else:' in such cases, and modify
the templates so that new modules do not have the same issue in the
future.
2023-09-11 12:01:17 -03:00
Rafael Guterres Jeffman
fd1352ad7e pylint: Disable warning when using non-literal dict
Recent pylint versions warns when a dictionaire is created using
'dict()' instead of '{...}'. Using 'dict()' in ansible-freeipa modules
actually enhances readability, so this change disables the check for
'use-dict-literal' in pylint.
2023-09-11 12:01:17 -03:00
Thomas Woerner
de38e8f0bc Merge pull request #1138 from rjeffman/fix_spec_update_modules
spec file: Updated list of modules
2023-09-11 10:55:31 +02:00
Rafael Guterres Jeffman
847ae2a374 spec file: Updated list of modules
Current spec file template was missing certificate, netgroup and
idview modules.
2023-09-08 11:21:33 -03:00
Thomas Woerner
bcee9aba92 Merge pull request #1136 from rjeffman/bump_ansible_version_2.13
Bump Ansible version to 2.13
2023-09-08 15:47:57 +02:00
Rafael Guterres Jeffman
c34c66fa79 ansible-freeipa: Bump minimum supported Ansible version to 2.13
As ansible-freeipa roles does not support Ansible 2.8, bump the
collection version to the currently oldest supported Ansible version
available, 2.13.
2023-09-08 09:59:11 -03:00
Rafael Guterres Jeffman
0a3cd06c6e README-*: Bump minimum supported Ansible version to 2.13
As ansible-freeipa roles do not support version 2.8 anymore, change the
minimum supported version to 2.13, which is the currently minimum
available and supported Ansible version.

This patch fixes documentation on all plugin READMEs, spec file and
module templates.
2023-09-08 09:59:11 -03:00
Rafael Guterres Jeffman
b5b22c3f7e roles: Bump minimum Ansible version to 2.13
Currently, the minimum supported Ansible version is 2.13, and
ansible-freeipa roles does not work with any version less than 2.9,
altough ansible-freeipa documentation states that the minimum version to
use is 2.8.

This patch fixes documentation and roles metadata to require that the
minimum Ansible version used is 2.13.
2023-09-08 09:59:11 -03:00
Thomas Woerner
7ee385ee02 Merge pull request #1131 from rjeffman/roles_update_supported_distros
Updated supported distros
2023-09-07 13:05:24 +02:00
Thomas Woerner
7d9e4da9df Merge pull request #1120 from rjeffman/ci_fix_sanity_test_ansible_lint
upstream ci: fix sanity test ansible lint failures
2023-09-07 13:03:53 +02:00
Thomas Woerner
0a20b5902d Merge pull request #1133 from rjeffman/testday_1
ipacert: Fix revocation example playbook on README
2023-09-07 13:02:13 +02:00
Rafael Guterres Jeffman
be9a2db404 Merge pull request #1134 from t-woerner/new_idview_module
New idview management module.
2023-09-06 09:53:23 -03:00
Thomas Woerner
ba4a360520 New idview management module.
There is a new idview management module placed in the plugins folder:

    plugins/modules/ipaidview.py

The idview module allows to ensure presence and absence of idviews and
idview host members.

Here is the documentation for the module:

    README-idview.md

New example playbooks have been added:

    playbooks/idview/idview-absent.yml
    playbooks/idview/idview-host-applied.yml
    playbooks/idview/idview-host-unapplied.yml
    playbooks/idview/idview-present.yml

New tests for the module can be found at:

    tests/idview/test_idview.yml
    tests/idview/test_idview_client_context.yml
2023-09-06 12:40:32 +02:00
Rafael Guterres Jeffman
3534fcdce7 ansible-lint: Use the same command line as galaxy-importer
Currently, there is a hard coded timeout in galaxy-importer that
prevents larger collections to execute the ansible-lint step [1].

This patch modifies the calls to ansible-lint on development tools and
upstream CI to use the same arguments as galaxy-importer and disables
the execution of the ansible-lint step for the Ansible's sanity test.

Requested ansible-lint version for tools is also updated, as a more
recent one is required.

This change will not allow development using an environment using Python
2.7, due to newer ansible-lint requirements. Roles and modules tests
against target nodes using Python 2.7 is still possible.

[1]: https://github.com/ansible/galaxy-importer/pull/231
2023-09-05 14:10:09 -03:00
Rafael Guterres Jeffman
f0f21fc8aa ipacert: Fix revocation example playbook on README
The revocation example playbook on README was wrong as it didn't have a
'reason' set, and the parameter must be used with 'state: revoked'.

This patch fixes the example and adds a new example using a reason
mnemonic instead of a reason number.
2023-08-24 08:50:18 -03:00
Rafael Guterres Jeffman
5ed96eda05 Updated supported distros
Updated all roles README files to add supported distros, as CentOS
Stream is supported (both 8 and 9) and also Debian clients.
2023-08-23 15:35:04 -03:00
Thomas Woerner
cf779e43bb Merge pull request #1123 from rjeffman/ci_increase_test_verbosity
ci: Increase verbosity for Ansible playbook runs
2023-07-24 10:05:25 +02:00
Rafael Guterres Jeffman
1a48a0fb63 Merge pull request #1122 from t-woerner/fix_ipa_command_invalid_param_choices_for_IPA_4_6
ansible_freeipa_module: Fix ipa_command_invalid_param_choices
2023-07-21 16:15:40 -03:00
Rafael Guterres Jeffman
ed3a0d5a1b ci: Increase verbosity for Ansible playbook runs
Some test failures requires more information than just the playbook
simple output. By increasing verbosity, the used parameters and the
failed line will be visible in the test error report, making it easier
to identify, reproduce and fix the issue.
2023-07-21 12:06:47 -03:00
Thomas Woerner
d58b492f1d ansible_freeipa_module: Fix ipa_command_invalid_param_choices
Fix ipa_command_invalid_param_choices for IPA 4.6 (RHEL-7)

- krbprincipalauthind in host_add does not have choices defined
- krbprincipalauthind in service_add does not have choices defined

api.Command[command].params[name].cli_metavar returns "STR" and
ast.literal_eval failes with a ValueError "malformed string".

There is no way to verify that the given values are valid or not in
this case. The check is done later on while applying the change
with host_add, host_mod, service_add and service_mod.
2023-07-21 16:44:04 +02:00
Thomas Woerner
88d4a36e17 Merge pull request #1055 from rjeffman/ipauser_idp_attrs
ipauser: Support for External IdP attributes.
2023-07-20 14:00:48 +02:00
Rafael Guterres Jeffman
6fa8223662 ipauser: Support for External IdP attributes.
Add support for 'idp' and 'idp_user_id' to ipauser plugin.

FreeIPA 4.10.0 is required for both attributes.
2023-07-19 14:38:30 -03:00
Rafael Guterres Jeffman
c9e8656494 Merge pull request #1119 from t-woerner/update_authtypes_authind_readmes
Update authtypes authind readmes
2023-07-19 11:37:34 -03:00
Thomas Woerner
a791c6a0ca README-user.md: Add choices pkinit, hardened and idp to user_auth_type
The parameter user_auth_type has been updated in FreeIPA. The choices
pkinit, hardened and idp have been missing and are now added.
2023-07-19 16:17:10 +02:00
Thomas Woerner
9cbccdade9 README-service.md: Add choice idp to auth_ind
The parameter auth_ind has been updated in FreeIPA. The choice
idp have been missing and is now added.
2023-07-19 16:07:50 +02:00
Thomas Woerner
42c07d6336 README-host.md: Add choice idp to auth_ind
The parameter auth_ind has been updated in FreeIPA. The choice
idp have been missing and is now added.
2023-07-19 16:07:27 +02:00
Thomas Woerner
a728a8d43e README-config.md: Add choices pkinit, hardened and idp to user_auth_type
The parameter user_auth_type has been updated in FreeIPA. The choices
pkinit, hardened and idp have been missing and are now added.
2023-07-19 16:07:19 +02:00
Thomas Woerner
bd3266e9f1 Merge pull request #1117 from rjeffman/doc_update_ubuntu_support
ipaserver: Update README with detailed Ubuntu support
2023-07-19 14:10:45 +02:00
Rafael Guterres Jeffman
48063d2b3a Merge pull request #1118 from t-woerner/update_authtypes_authind
Update authtypes authind
2023-07-19 08:59:18 -03:00
Thomas Woerner
5d08214516 Merge pull request #1075 from rjeffman/automount_indirect_maps
ipaautomountmap: add support for indirect maps
2023-07-19 13:53:55 +02:00
Rafael Guterres Jeffman
ef0b7e80f0 ipaserver: Update README with detailed Ubuntu support
Ubuntu does not have a FreeIPA server package since version 20.04. As
versions 16.04 (Xenial Xerus) and 18.04 (Bionic Beaver) will be
supported by Canonical until 2026 and 2028, repectively, we should keep
existing support for both versions in the ipaserver, ipareplica and
ipabackup roles until them.

This patch changes documentation to reflect that only those versions are
supported.
2023-07-19 08:51:06 -03:00
Rafael Guterres Jeffman
a33fcf45f8 ipaautomountmap: add support for indirect maps
Indirect maps were not supported by ansible-freeipa ipaautomountmap.
This patch adds support for adding indirect automount maps using the
"parent" and "mount" parameters, if the map do not yet exist. An
existing map cannot be modified.

The "parent" parameter must match an existing automount map, and the
"mount" parameter is required if "parent" is used.

A new example playbook can be found at:

    playbooks/automount/automount-map-indirect-map.yml

A new test playbook was added to test the feature:

    tests/automount/test_automountmap_indirect.yml
2023-07-19 08:41:25 -03:00
Thomas Woerner
c4b273c896 ipauser: Add choices pkinit, hardened and idp to user_auth_type
The parameter user_auth_type has been updated in FreeIPA. The choices
pkinit, hardened and idp have been missing and are now added.

An additional check was added to verify that the values of the
user_auth_type list are valid for the used IPA version.
2023-07-19 11:38:14 +02:00
Thomas Woerner
62d34d0a22 ipaservice: Add choice idp to auth_ind
The parameter auth_ind has been updated in FreeIPA. The choice
idp have been missing and is now added.

An additional check was added to verify that the values of the
auth_ind list are valid for the used IPA version.
2023-07-19 11:38:14 +02:00
Thomas Woerner
3ed0c229c4 ipahost: Add choice idp to auth_ind
The parameter auth_ind has been updated in FreeIPA. The choice
idp have been missing and is now added.

An additional check was added to verify that the values of the
auth_ind list are valid for the used IPA version.
2023-07-19 11:38:14 +02:00
Thomas Woerner
c089c010e6 ipaconfig: Add choices pkinit, hardened and idp to user_auth_type
The parameter user_auth_type has been updated in FreeIPA. The choices
pkinit, hardened and idp have been missing and are now added.

An additional check was added to verify that the values of the
user_auth_type list are valid for the used IPA version.
2023-07-19 11:38:14 +02:00
Thomas Woerner
cfbdd83a64 ansible_freeipa_module: New ipa_command_invalid_param_choices method
New IPAAnsibleModule.ipa_command_invalid_param_choices method to return
invalid parameter choices for an IPA command.

This is needed to verify for example if userauthtype and authind are
supporting the idp value.
2023-07-19 11:38:06 +02:00
Thomas Woerner
fef1bdcf8e Merge pull request #1116 from rjeffman/fix_runtests_collections
utils/run-tests.sh: Install Ansible collections on virtual environment
2023-07-17 15:35:54 +02:00
Thomas Woerner
411d363d91 Merge pull request #1056 from rjeffman/ipauser_smb_params
ipauser: Add support for SMB attributes.
2023-07-17 15:24:22 +02:00
Rafael Guterres Jeffman
1555132d85 utils/run-tests.sh: Install Ansible collections on virtual environment
When runing tests using 'utils/run-tests.sh' from inside an existing
Python virtual environment the Ansible collections are not installed due
to the order of execution of the script. On a machine that does not have
the 'containers.*' collection the test fails as there is no container
connector available.

This patch moves the section that installs Ansible collections to run
after the virtual environment is configured, and then install the
collections (usually, only 'containers.podman'), allowing the tests to
be executed.
2023-07-15 14:55:44 -03:00
Rafael Guterres Jeffman
57ad57dda3 ipauser: Add support for SMB attributes.
Since FreeIPA version 4.8.0 ipauser has support for smb-logon-script,
smb-profile-path, smb-home-dir, and smb-home-drive drive attributes.

On FreeIPA, these attributes are only available when modifying a user,
so if the user defined in the playbook does not exist, two calls to IPA
API are executed, a 'user_add' followed by a 'user_mod'.
(see https://github.com/freeipa/freeipa/blob/master/doc/designs/adtrust/samba-domain-controller.md

A new example playbook can be found at:

     playbooks/user/smb-attributes.yml

A new test playbook can be found at:

     tests/user/test_user_smb_attrs.yml
2023-07-14 10:53:30 -03:00
Thomas Woerner
dab64c7cf6 Merge pull request #1098 from rjeffman/doc_diferentiate_location_host_and_server
doc: Differentiate location meaning between host and server
2023-07-14 15:39:37 +02:00
Rafael Guterres Jeffman
b7145bc2cc doc: Differentiate location meaning between host and server
Host location and server location have very different meanings in IPA.
ipahost uses 'location' as an optional hint to where the host may be
physically located, ipaserever uses location to identify which DNS
location the server is part of.

This change updates documentation to make attribute description more
clear. Surrounding text have been changed to match text style as used in
other plugins.

This patch is related to: https://github.com/freeipa/freeipa/pull/6840
2023-07-14 10:25:51 -03:00
Thomas Woerner
c9f1da5d6b Merge pull request #1076 from rjeffman/fix_usercheck_dictcheck
Fix handling of ipapwpolicy attributes usercheck and dictcheck
2023-07-14 15:25:20 +02:00
Thomas Woerner
f4070f6a30 Merge pull request #1100 from rjeffman/ci_update_ansible_2_15
upstream CI: Update ansible-core version
2023-07-14 15:11:21 +02:00
Thomas Woerner
ad9a03ece6 Merge pull request #1114 from rjeffman/remove_virtualenv
Remove dependency on 'virtualenv'
2023-07-14 15:05:09 +02:00
Rafael Guterres Jeffman
1bfe6888a4 Remove dependency on 'virtualenv'
'virtualenv' is an external dependency with the same purpose of Python's
'venv' module. This patch removes the external dependency in favor of
the readily available package.
2023-07-13 15:07:08 -03:00
Thomas Woerner
51ddaa6491 Merge pull request #1044 from rjeffman/ipauser_street
ipauser: Add support for parameter "street"
2023-07-12 20:34:49 +02:00
Rafael Guterres Jeffman
f56861cc15 ipauser: Add support for parameter "street"
ipauser plugin was missing user parameter "street".

Tests were updated to reflect the new parameter.
2023-07-12 12:31:26 -03:00
Thomas Woerner
c4de680497 Merge pull request #1039 from rjeffman/ipauser_gecos
ipauser: Add support to modify GECOS field.
2023-07-12 17:08:53 +02:00
Rafael Guterres Jeffman
7b2701b985 ipapwpolicy: Updated module documentation.
Most of ipapwpolicy parameters can be set to an empty string ("") so
that the policy is not applied to pwpolicy. This was not refelected on
the documentation.

This change adds 'or ""' to all the fields that can be disabled by
setting it to an empty string. Also, `data types were reviewed and fixed.
2023-07-11 10:15:43 -03:00
Rafael Guterres Jeffman
694c717829 ipapwpolicy: Modify handling of usercheck and dictcheck
Modified handling of boolean values by using Ansible's 'boolean()' check
function so that a string can be used and either a bool value is
accepted or an empty string.

As the error message was changed to use the same Ansible message, tests
were also updated.
2023-07-11 10:15:43 -03:00
Rafael Guterres Jeffman
083396e133 module_utils: Export Ansible's 'boolean' parsing function.
Export Ansible's 'boolean' parsing function so it can be used to verify
if a string can be handled as a truthy value, allowing module parameters
to use strings instead of bools, as strings can be cleared by using
empty strings.
2023-07-11 08:33:35 -03:00
Rafael Guterres Jeffman
9a8a1db38f ipauser: Add support to modify GECOS field.
This patch adds a new parameter to ipauser, 'gecos', which can be used
to set the 'gecos' field of an IPA user. The default behavior of
automatically set the GECOS field to "<first> <last>" is not modified,
it is only possible to change the field to a custom value.

No validation on the value provided is done, as it is with FreeIPA.
2023-07-10 14:34:44 -03:00
Rafael Guterres Jeffman
8f9c344bc1 Merge pull request #1106 from renich/patch-1
Singular to plural on random serial numbers setting
2023-06-15 11:15:55 -03:00
Renich Bon Ciric
067b683b81 Singular to plural on random serial numbers setting
The setting was in singular in the example while being documented in plural form.
2023-06-14 16:53:22 -06:00
Rafael Guterres Jeffman
51f64e4393 upstream CI: Update ansible-core version
ansible-core 2.15 has been released on May 15th, 2023, and version 2.12
has reached EOL on May 22nd, 2023.

This patch updates the ansible-core versions used on upstream CI tests
to reflect Ansible's new releases.
2023-06-09 10:05:47 -03:00
Rafael Guterres Jeffman
45700bc02b Merge pull request #1082 from t-woerner/fix_pwpolicy_maxsequence_test
pwpolicy test: Fix maxsequence test
2023-06-07 12:35:00 -03:00
Thomas Woerner
d04a12e522 pwpolicy test: Fix maxsequence test
The maxsequence test was testing maxrepeat. Therefore the typo reported
with https://github.com/freeipa/ansible-freeipa/pull/1081 was never
seen.

The test has been fixed.
2023-06-07 17:17:20 +02:00
Thomas Woerner
4e9ec11b23 Merge pull request #1081 from cutrightjm/patch-1
Fix typo in ipapwpolicy.py
2023-06-07 17:17:01 +02:00
Thomas Woerner
2d93051101 Merge pull request #1078 from rjeffman/ipapwpolicy_simple_attribute_test
ipapwpolicy: simplified and faster attribute verification
2023-06-07 17:12:36 +02:00
Jacob Cutright
1a7b279d78 Fix typo in ipapwpolicy.py
The 'maxsequence' attribute was never applied as there was a typo when
it was set. By fixing the field name, 'maxsequence' is correclty set.

The failure was not seen before due to missing tests. The tests will be
added in a separate PR.
2023-06-07 12:04:49 -03:00
Thomas Woerner
be228d1df3 Merge pull request #1094 from rjeffman/ci_disable_pytests
Upstream CI: Disable execution of pytest tests
2023-06-07 17:00:59 +02:00
Thomas Woerner
ce95c638be Merge pull request #1099 from rjeffman/roles_disallow_fqdn_domain_match
Don't allow the FQDN to match the domain on server installs
2023-06-07 16:56:06 +02:00
Thomas Woerner
876f39a6c5 Merge pull request #687 from yrro/ipacert
ipacert module
2023-06-07 16:54:47 +02:00
Rafael Guterres Jeffman
950840e050 Merge pull request #1101 from t-woerner/multiple_service_management
Multiple service management
2023-06-07 11:53:14 -03:00
Sam Morris
87e1edf575 New certificate management module.
There is a new certificate management module placed in the plugins
folder:

    plugins/modules/ipacert.py

The certificate module allows to request, revoke, release and retrieve
certificates for users, hosts and services.

Here is the documentation for the module:

    README-cert.md

New example playbooks have been added:

    playbooks/cert/cert-hold.yml
    playbooks/cert/cert-release.yml
    playbooks/cert/cert-request-host.yml
    playbooks/cert/cert-request-service.yml
    playbooks/cert/cert-request-user.yml
    playbooks/cert/cert-retrieve.yml
    playbooks/cert/cert-revoke.yml

New tests for the module can be found at:

    tests/cert/test_cert_client_context.yml
    tests/cert/test_cert_host.yml
    tests/cert/test_cert_service.yml
    tests/cert/test_cert_user.yml

The module has been co-authored by Sam Morris (@yrro) and Rafael
Guterres Jeffman (@rjeffman).
2023-06-07 11:35:25 -03:00
Thomas Woerner
09250cb2c5 ipaservice: Updated and new tests for certificates and multi service handling
The tests test_services_absent.yml, test_services_present.yml and
test_services_present_slice.yml have been updated to use in memory data
for testing instead of loading json files. This made is simpler to use
variables from the playbook for example for fqdn host names.

New tests for certificates with and without trailing new lines have been
added for single service and multiple service handling.
2023-06-07 13:36:48 +02:00
Thomas Woerner
872c9e4cb2 ipaservice: Add Denis Karpelevich to the authors header
Denis added the multi service handling code. Therefore he should be
listed in the file header.
2023-06-07 13:36:48 +02:00
Thomas Woerner
efe9c68600 ipaservice: Properly Handle certs with leading or trailing white space
Any leading or trailing whitespace is removed while adding the
certificates with serive_add_cert. To be able to compare the results
from service_show with the given certificates we have to remove the
white space also.
2023-06-07 13:36:28 +02:00
Denis Karpelevich
0d9873b81c Allow multiple services creation
Adding an option to create multiple services in one go.
Adding tests (present/absent/without_skip_host_check)

Copied from PR #1054

Signed-off-by: Denis Karpelevich <dkarpele@redhat.com>
2023-06-06 12:40:33 +02:00
Rafael Guterres Jeffman
5b91703bd7 Don't allow the FQDN to match the domain on server installs
If server FQDN matches the domain name, the installation will succeed,
but DNS records will not work. If 'setup_dns: true' is used, there will
be no A record for the host, only a NS record, and the PTR record will
point to the domain name.

Based on: https://github.com/freeipa/freeipa/pull/6853
Related to: https://pagure.io/freeipa/issue/9003
2023-06-05 12:56:47 -03:00
Rafael Guterres Jeffman
180afd7586 Merge pull request #1077 from rjeffman/update_gitignore
Make Git ignore temporary and output files.
2023-05-30 10:59:09 -03:00
Thomas Woerner
7f16914032 Merge pull request #1097 from rjeffman/fix_ansible_lint_var_naming
upstream CI: Disable ansible-lint var-naming check
2023-05-30 14:45:14 +02:00
Rafael Guterres Jeffman
306522acd8 upstream CI: Disable ansible-lint var-naming check
Latest ansible-lint version (6.16.1) started to raise an error when
variable names from within roles are not prefixed with  the role name.
Error: var-naming[no-role-prefix].

As Ansible sanity check does not enforce this, it will be disabled, for
now on ansible-freeipa's upstream CI.

A future effort to reduce the checks that are not being evaluated should
be done as preparation for future Ansible Galaxy and Automation Hub
requirements.
2023-05-16 16:08:51 -03:00
Rafael Guterres Jeffman
a155324188 Upstream CI: Disable execution of pytest tests.
The tests under 'tests/pytests' were a POC to bring tests that evaluate
the result of playbook execution on the IPA environment. This is
currently only implemented for dnszone tests, and similar test coverage
is obtained with other tests.

As there is an ongoing issue with Ansible's docker pluging
("the connection plugin 'docker' was not found"), which is stil under
investigation, by removing the pytest tests we'll remove the consistent
failures currently seen on upstream CI, and will not loose test
coverage, specially if we take into account downstream tests.

Also, a new version for the pytests will be available once multihost
testing is implemented for upstream.
2023-05-15 15:41:09 -03:00
Rafael Guterres Jeffman
8ec5b1fe21 Merge pull request #1092 from t-woerner/fix_requests_version_require_for_build_container
tests/azure/templates/build_container.yml: Quote requests with version
2023-05-08 11:41:38 -03:00
Thomas Woerner
316255d524 tests/azure/templates/build_container.yml: Quote requests with version
The version requirement for requests need to be quoted not to lead into
a pip install command issue.

This is related to PR #1089 (Pin requests to < 2.29 temporarily)
2023-05-08 16:28:20 +02:00
Rafael Guterres Jeffman
36b7a18e40 Merge pull request #1088 from t-woerner/fix_new_ansible_lint_disallowed_ignores
Fix new ansible lint disallowes ignores
2023-05-05 12:08:41 -03:00
Thomas Woerner
a32fcb3765 ansible_freeipa_module.py: Calm down ansible-test on print and sys.exit
The function exit_raw_json is a replacement for AnsibleModule.exit_json
without flterting out values for no_log parameters.

Ansible added checks for pylint to forbid print and also sys.exit and
fails with ansible-bad-function. As the check is not known outside of
ansible-test, the disable line needed also W0012:

    # pylint: disable=W0012,ansible-bad-function
2023-05-05 16:56:38 +02:00
Thomas Woerner
2d4cad6c1b ipaserver_test.py: Add missing default for random_serial_numbers
random_serial_numbers was missing the default value in the DOCMENTATION
section.
2023-05-05 16:56:38 +02:00
Thomas Woerner
a4b8e10a40 ansible-test: Do not use automatic field numbering specification
Automatic field numbering specification is not allowed by ansible-test.
2023-05-05 16:26:45 +02:00
Thomas Woerner
98681bd4d2 Use "#!/usr/bin/env python" for python shebang
ansible is not allowing to use "#!/usr/bin/python".

Due to a change in ansible-lint it is not possible to ignore the "bad"
shebang.
2023-05-05 16:26:45 +02:00
Thomas Woerner
2882e2426a Add -eu to all bash shebangs
ansible requires to either use "#!/bin/bash -eu" or "#!/bin/bash -eux"
for bash shebangs.
2023-05-05 16:26:45 +02:00
Thomas Woerner
f056775d95 Remove old or empty sanity ignore files
The old ignore file ignore-2.12.txt is not needed and used anymore. The
new files ignore-2.13.txt and ignore-2.14.txt are empty after
ansible-lint made nearly all ignores disallowed.

All the newly disallowed ignores need to be fixed.

See https://github.com/ansible/ansible-lint/pull/3102
2023-05-05 16:26:45 +02:00
Rafael Guterres Jeffman
ad5450cd6f Merge pull request #1089 from t-woerner/pin_requests_below_2_29
Pin requests to < 2.29 temporarily
2023-05-05 11:25:39 -03:00
Thomas Woerner
e75d82131d Pin requests to < 2.29 temporarily
Due to https://github.com/docker/docker-py/issues/3113 requests need to
be pinned below 2.29 as a temporary solution.
2023-05-05 15:06:38 +02:00
Rafael Guterres Jeffman
99e468ad60 Merge pull request #1083 from t-woerner/fix_azure_molecule_docker
tests/azure: Install molecule-plguins to get docker driver
2023-04-27 17:45:35 -03:00
Thomas Woerner
3cc111782c tests/azure: Install molecule-plguins to get docker driver
The docker driver is not part of molecule 5.0.0 anymore.
molecule-plugins need to be installed to get the driver.
2023-04-27 14:01:09 +02:00
Rafael Guterres Jeffman
b429b4495e Merge pull request #1035 from t-woerner/new_module_github_user_fix
Fixes and enhancements for utils/new_module and templates
2023-04-20 10:03:19 -03:00
Rafael Guterres Jeffman
0f99ef2199 Merge pull request #1080 from t-woerner/module_defaults
Create action group in collection for use with module_defaults
2023-04-20 10:03:10 -03:00
Thomas Woerner
1c8f1c28e1 utils/templates/test_module*.yml.in: Use generic module_defaults
The usage of module_defaults allows to reduce the size of the tests and
to have the needed information in the tasks only. The default values for the
parameters are automatically passed to the module by Ansible.

It is not possible to use a module group for module_defaults as this could
only be done with Ansible Collections. The tests are also used upstream and
downstream without a collection.

Without groups of a collection it is needed to add the defaults for all
modules separately.

Simple example:

    module_defaults:
      ipahost:
        ipaadmin_password: SomeADMINpassword
        ipaapi_context: "{{ ipa_context | default(omit) }}"

Several module example using YAML anchors and aliases:

    module_defaults:
      ipahost: &ipa_module_defaults
        ipaadmin_password: SomeADMINpassword
        ipaapi_context: "{{ ipa_context | default(omit) }}"
      ipauser: *ipa_module_defaults
      ipagroup: *ipa_module_defaults
2023-04-20 10:10:51 +02:00
Thomas Woerner
47d5211185 utils/templates/test_module*.yml.in: Better docs for become and gather_facts
The documentation for "become" and "gather_facts" has been updated to
make sure that these parameters are enabled only in new tests if it is
really needed.
2023-04-20 10:10:51 +02:00
Thomas Woerner
4a18ad03c8 utils/templates/{README*.md.in,test_module*.yml.in}: Use true and false
The values "yes" and "no" will not be valid in the future for bool
parameters. Therefore "yes" and "no" have been replaced by "true" and
"false".
2023-04-20 10:09:07 +02:00
Thomas Woerner
966797dbee utils/build-galaxy-release.sh: Create module action group
The module action group <collection-prefix>.modules is created
automatically while building the galaxy release.

The action group can be used for module_defaults in this way:

    module_defauls:
      group/<collection-prefix>.modules:
        ipaadmin_password: SomeADMINpassword

Example:

    module_defaults:
      group/freeipa.ansible_freeipa.modules:
        ipaadmin_password: SomeADMINpassword
        ipaapi_context: "{{ ipa_context | default(omit) }}"
    collections:
    - freeipa.ansible_freeipa
2023-04-20 10:04:41 +02:00
Thomas Woerner
892c0dd6f0 utils/galaxyfy.py: Handle module_defaults, match roles and modules
The section module_defaults was not handled by utils/galaxyfy.py, also
there was no verification that only roles and modules provided by
ansible-freeipa are matched for prepending the collection prefix.
2023-04-20 10:04:26 +02:00
Rafael Guterres Jeffman
645a234d92 Make Git ignore temporary and output files.
Ignore vim .swp files and files generated by creating ansible-freeipa
collection, when checking repository status.
2023-04-18 10:21:24 -03:00
Thomas Woerner
5cbc8b7ada New utils/facts.py: Provide facts about the repo like role and module lists
The list of modules and roles is needed in several scripts now,
therefore it makes sense to have one place for this.

Here are the current variables:

BASE_DIR:           Base directory of the repo
ROLES:              List of roles in the roles folder
MANAGEMENT_MODULES: List of management modules in the plugins/modules
                    folder
ROLES_MODULES:      List of modules in the roles/*/library folders
ALL_MODULES:        List of all modules, the management and the roles
                    modules

All lists are sorted.
2023-04-18 13:36:42 +02:00
Thomas Woerner
5e5fbd87bf utils/templates/ipamodule.py.in: Add missing bracket
The parameter argument spec of name was missing the closing bracket. The
bracket has been added.
2023-04-14 17:23:37 +02:00
Rafael Guterres Jeffman
35ded3bf53 utils/new_module: Ensure correct number of parameters for new_module
When testing the number parameters for new_module, the
`github_user` was not being taken into account.
2023-04-14 17:23:37 +02:00
Thomas Woerner
209c6365ea utils/new_module: Fix github_user test
new_module was always failing with "github_user is not valid". The wrong
variable was checked: $githubuser instead of $github_user.
2023-04-14 17:23:37 +02:00
Rafael Guterres Jeffman
a69446021b ipapwpolicy: simplified and faster attribute verification
Use a simpler and faster 'any()' test instead of creating two lists and
checking if resulting list is empty.
2023-04-11 18:45:49 -03:00
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
308 changed files with 15110 additions and 2172 deletions

View File

@@ -35,6 +35,7 @@ skip_list:
- yaml # yamllint should be executed separately.
- experimental # Do not run any experimental tests
- name[template] # Allow Jinja templating inside task names
- var-naming
use_default_rules: true

View File

@@ -8,10 +8,8 @@ jobs:
name: Verify ansible-test sanity
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v3.1.0
- uses: actions/checkout@v4.1.1
with:
fetch-depth: 0
- name: Install virtualenv using pip
run: pip install virtualenv
- name: Run ansible-test
run: bash tests/sanity/sanity.sh

View File

@@ -5,30 +5,13 @@ on:
- pull_request
jobs:
check_docs_oldest_supported:
name: Check Ansible Documentation with ansible-core 2.12.
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.12
run: |
python -m pip install "ansible-core >=2.12,<2.13"
- name: Run ansible-doc-test
run: |
ANSIBLE_LIBRARY="." ANSIBLE_DOC_FRAGMENT_PLUGINS="." python utils/ansible-doc-test -v roles plugins
check_docs_previous:
name: Check Ansible Documentation with ansible-core 2.13.
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v3.1.0
- uses: actions/checkout@v4.1.1
with:
fetch-depth: 0
- uses: actions/setup-python@v4.3.0
fetch-depth: 1
- uses: actions/setup-python@v5.1.0
with:
python-version: '3.x'
- name: Install Ansible 2.13
@@ -38,14 +21,14 @@ jobs:
run: |
ANSIBLE_LIBRARY="." ANSIBLE_DOC_FRAGMENT_PLUGINS="." python utils/ansible-doc-test -v roles plugins
check_docs_current:
check_docs_previous:
name: Check Ansible Documentation with ansible-core 2.14.
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v3.1.0
- uses: actions/checkout@v4.1.1
with:
fetch-depth: 0
- uses: actions/setup-python@v4.3.0
fetch-depth: 1
- uses: actions/setup-python@v5.1.0
with:
python-version: '3.x'
- name: Install Ansible 2.14
@@ -55,14 +38,31 @@ jobs:
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.15.
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4.1.1
with:
fetch-depth: 1
- uses: actions/setup-python@v5.1.0
with:
python-version: '3.x'
- name: Install Ansible 2.15
run: |
python -m pip install "ansible-core >=2.15,<2.16"
- 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:
- uses: actions/checkout@v3.1.0
- uses: actions/checkout@v4.1.1
with:
fetch-depth: 0
- uses: actions/setup-python@v4.3.0
fetch-depth: 1
- uses: actions/setup-python@v5.1.0
with:
python-version: '3.x'
- name: Install Ansible-latest

View File

@@ -8,27 +8,27 @@ jobs:
name: Verify ansible-lint
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v3.1.0
- uses: actions/checkout@v4.1.1
with:
fetch-depth: 0
- uses: actions/setup-python@v4.3.0
- uses: actions/setup-python@v5.1.0
with:
python-version: "3.x"
- name: Run ansible-lint
run: |
pip install "ansible-core >=2.14,<2.15" ansible-lint
pip install "ansible-core>=2.16,<2.17" 'ansible-lint==6.22'
utils/build-galaxy-release.sh -ki
cd .galaxy-build
ansible-lint
ansible-lint --profile production --exclude tests/integration/ --exclude tests/unit/ --parseable --nocolor
yamllint:
name: Verify yamllint
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v3.1.0
- uses: actions/checkout@v4.1.1
with:
fetch-depth: 0
- uses: actions/setup-python@v4.3.0
fetch-depth: 1
- uses: actions/setup-python@v5.1.0
with:
python-version: "3.x"
- name: Run yaml-lint
@@ -38,10 +38,10 @@ jobs:
name: Verify pydocstyle
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v3.1.0
- uses: actions/checkout@v4.1.1
with:
fetch-depth: 0
- uses: actions/setup-python@v4.3.0
fetch-depth: 1
- uses: actions/setup-python@v5.1.0
with:
python-version: "3.x"
- name: Run pydocstyle
@@ -53,10 +53,10 @@ jobs:
name: Verify flake8
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v3.1.0
- uses: actions/checkout@v4.1.1
with:
fetch-depth: 0
- uses: actions/setup-python@v4.3.0
fetch-depth: 1
- uses: actions/setup-python@v5.1.0
with:
python-version: "3.x"
- name: Run flake8
@@ -68,23 +68,23 @@ jobs:
name: Verify pylint
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v3.1.0
- uses: actions/checkout@v4.1.1
with:
fetch-depth: 0
- uses: actions/setup-python@v4.3.0
fetch-depth: 1
- uses: actions/setup-python@v5.1.0
with:
python-version: "3.x"
- name: Run pylint
run: |
pip install pylint==2.14.4 wrapt==1.14.0
pip install 'pylint>=3.0'
pylint plugins roles --disable=import-error
shellcheck:
name: Shellcheck
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v3.1.0
- uses: actions/checkout@v4.1.1
with:
fetch-depth: 0
fetch-depth: 1
- name: Run ShellCheck
uses: ludeeus/action-shellcheck@master

View File

@@ -8,9 +8,9 @@ jobs:
name: Verify readme
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v3.1.0
- uses: actions/checkout@v4.1.1
with:
fetch-depth: 0
fetch-depth: 1
- name: Run readme test
run: |
error=0

6
.gitignore vendored
View File

@@ -1,5 +1,11 @@
*.pyc
*.retry
*.swp
# collection files
freeipa-ansible_freeipa*.tar.gz
redhat-rhel_idm*.tar.gz
importer_result.json
# ignore virtual environments
/.tox/

View File

@@ -1,30 +1,40 @@
---
repos:
- repo: https://github.com/ansible/ansible-lint.git
rev: v6.6.1
rev: v24.5.0
hooks:
- id: ansible-lint
always_run: false
pass_filenames: true
files: \.(yaml|yml)$
exclude: /env[^/]*.(yaml|yml)$
entry: |
env ANSIBLE_LIBRARY=./plugins/modules ANSIBLE_MODULE_UTILS=./plugins/module_utils ANSIBLE_DOC_FRAGMENT_PLUGINS=./plugins/doc_fragments ansible-lint
entry: |-
env
ANSIBLE_LIBRARY=./plugins/modules
ANSIBLE_MODULE_UTILS=./plugins/module_utils
ANSIBLE_DOC_FRAGMENT_PLUGINS=./plugins/doc_fragments
ansible-lint
--offline
--profile production
--exclude tests/integration/
--exclude tests/unit/
--parseable
--nocolor
- repo: https://github.com/adrienverge/yamllint.git
rev: v1.28.0
rev: v1.35.1
hooks:
- id: yamllint
files: \.(yaml|yml)$
- repo: https://github.com/pycqa/flake8
rev: 5.0.3
rev: 7.0.0
hooks:
- id: flake8
- repo: https://github.com/pycqa/pydocstyle
rev: 6.0.0
rev: 6.3.0
hooks:
- id: pydocstyle
- repo: https://github.com/pycqa/pylint
rev: v2.14.4
rev: v3.2.2
hooks:
- id: pylint
args:

View File

@@ -22,7 +22,7 @@ Requirements
------------
**Controller**
* Ansible version: 2.8+
* Ansible version: 2.15+
**Node**
* Supported FreeIPA version (see above)

View File

@@ -21,7 +21,7 @@ FreeIPA versions 4.4.0 and up are supported by the ipaautomountkey module.
Requirements
------------
**Controller**
* Ansible version: 2.8+
* Ansible version: 2.15+
**Node**
* Supported FreeIPA version (see above)

View File

@@ -21,7 +21,7 @@ FreeIPA versions 4.4.0 and up are supported by the ipaautomountlocation module.
Requirements
------------
**Controller**
* Ansible version: 2.8+
* Ansible version: 2.15+
**Node**
* Supported FreeIPA version (see above)

View File

@@ -21,7 +21,7 @@ FreeIPA versions 4.4.0 and up are supported by the ipaautomountmap module.
Requirements
------------
**Controller**
* Ansible version: 2.8+
* Ansible version: 2.15+
**Node**
* Supported FreeIPA version (see above)
@@ -54,6 +54,21 @@ Example playbook to ensure presence of an automount map:
desc: "this is a map for servers in the DMZ"
```
Automount maps can contain a submount key, which defines a mount location within the map the references another map. On FreeIPA, this is known as an indirect map. An indirect automount map is equivalent to adding a proper automount key to a map, referencyng another map (this second map is the indirect map). Use `parent` and `mount` parameters to create an indirect automount map with ansible-freeipa, without the need to directly manage the automount keys.
Example playbook to ensure an indirect automount map is present:
```yaml
---
- name: Playbook to add an indirect automount map
ipaautomountmap:
ipaadmin_password: SomeADMINpassword
name: auto.indirect
location: DMZ
parent: auto.DMZ
mount: dmz_indirect
```
Example playbook to ensure auto.DMZi is absent:
```yaml
@@ -81,16 +96,14 @@ Variable | Description | Required
`ipaadmin_password` | The admin password is a string and is required if there is no admin ticket available on the node | no
`name` \| `mapname` \| `map` \| `automountmapname` | Name of the map to manage | yes
`location` \| `automountlocation` \| `automountlocationcn` | Location name. | yes
`parentmap` | Parent map of the indirect map. Can only be used when creating new maps. Default: auto.master | no
`mount` | Indirect map mount point, relative to parent map. | yes, if `parent` is used.
`desc` \| `description` | Description of the map | yes
`state` | The state to ensure. It can be one of `present`, or `absent`, default: `present`. | no
Notes
=====
Creation of indirect mount points are not supported.
Authors
=======
Chris Procter
- Chris Procter
- Rafael Jeffman

192
README-cert.md Normal file
View File

@@ -0,0 +1,192 @@
Cert module
============
Description
-----------
The cert module makes it possible to request, revoke and retrieve SSL certificates for hosts, services and users.
Features
--------
* Certificate request
* Certificate hold/release
* Certificate revocation
* Certificate retrieval
Supported FreeIPA Versions
--------------------------
FreeIPA versions 4.4.0 and up are supported by the ipacert module.
Requirements
------------
**Controller**
* Ansible version: 2.15+
* Some tool to generate a certificate signing request (CSR) might be needed, like `openssl`.
**Node**
* Supported FreeIPA version (see above)
Usage
=====
Example inventory file
```ini
[ipaserver]
ipaserver.test.local
```
Example playbook to request a new certificate for a service:
```yaml
---
- name: Certificate request
hosts: ipaserver
tasks:
- name: Request a certificate for a web server
ipacert:
ipaadmin_password: SomeADMINpassword
state: requested
csr: |
-----BEGIN CERTIFICATE REQUEST-----
MIGYMEwCAQAwGTEXMBUGA1UEAwwOZnJlZWlwYSBydWxlcyEwKjAFBgMrZXADIQBs
HlqIr4b/XNK+K8QLJKIzfvuNK0buBhLz3LAzY7QDEqAAMAUGAytlcANBAF4oSCbA
5aIPukCidnZJdr491G4LBE+URecYXsPknwYb+V+ONnf5ycZHyaFv+jkUBFGFeDgU
SYaXm/gF8cDYjQI=
-----END CERTIFICATE REQUEST-----
principal: HTTP/www.example.com
register: cert
```
Example playbook to revoke an existing certificate:
```yaml
---
- name: Revoke certificate
hosts: ipaserver
tasks:
- name Revoke a certificate
ipacert:
ipaadmin_password: SomeADMINpassword
serial_number: 123456789
reason: 5
state: revoked
```
When revoking a certificate a mnemonic can also be used to set the revocation reason:
```yaml
---
- name: Revoke certificate
hosts: ipaserver
tasks:
- name Revoke a certificate
ipacert:
ipaadmin_password: SomeADMINpassword
serial_number: 123456789
reason: cessationOfOperation
state: revoked
```
Example to hold a certificate (alias for revoking a certificate with reason `certificateHold (6)`):
```yaml
---
- name: Hold a certificate
hosts: ipaserver
tasks:
- name: Hold certificate
ipacert:
ipaadmin_password: SomeADMINpassword
serial_number: 0xAB1234
state: held
```
Example playbook to release hold of certificate (may be used with any revoked certificates, despite of the rovoke reason):
```yaml
---
- name: Release hold
hosts: ipaserver
tasks:
- name: Take a revoked certificate off hold
ipacert:
ipaadmin_password: SomeADMINpassword
serial_number: 0xAB1234
state: released
```
Example playbook to retrieve a certificate and save it to a file in the target node:
```yaml
---
- name: Retriev certificate
hosts: ipaserver
tasks:
- name: Retrieve a certificate and save it to file 'cert.pem'
ipacert:
ipaadmin_password: SomeADMINpassword
certificate_out: cert.pem
state: retrieved
```
ipacert
-------
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
`csr` | X509 certificate signing request, in PEM format. | yes, if `state: requested`
`principal` | Host/service/user principal for the certificate. | yes, if `state: requested`
`add` \| `add_principal` | Automatically add the principal if it doesn't exist (service principals only). (bool) | no
`profile_id` \| `profile` | Certificate Profile to use | no
`ca` | Name of the issuing certificate authority. | no
`chain` | Include certificate chain in output. (bool) | no
`serial_number` | Certificate serial number. (int) | yes, if `state` is `retrieved`, `held`, `released` or `revoked`.
`revocation_reason` \| `reason` | Reason for revoking the certificate. Use one of the reason strings, or the corresponding value: "unspecified" (0), "keyCompromise" (1), "cACompromise" (2), "affiliationChanged" (3), "superseded" (4), "cessationOfOperation" (5), "certificateHold" (6), "removeFromCRL" (8), "privilegeWithdrawn" (9), "aACompromise" (10) | yes, if `state: revoked`
`certificate_out` | Write certificate (chain if `chain` is set) to this file, on the target node. | no
`state` | The state to ensure. It can be one of `requested`, `held`, `released`, `revoked`, or `retrieved`. `held` is the same as revoke with reason "certificateHold" (6). `released` is the same as `cert-revoke-hold` on IPA CLI, releasing the hold status of a certificate. | yes
Return Values
=============
Values are returned only if `state` is `requested` or `retrieved` and if `certificate_out` is not defined.
Variable | Description | Returned When
-------- | ----------- | -------------
`certificate` | Certificate fields and data. (dict) <br>Options: | if `state` is `requested` or `retrieved` and if `certificate_out` is not defined
&nbsp; | `certificate` - Issued X509 certificate in PEM encoding. Will include certificate chain if `chain: true`. (list) | always
&nbsp; | `san_dnsname` - X509 Subject Alternative Name. | When DNSNames are present in the Subject Alternative Name extension of the issued certificate.
&nbsp; | `issuer` - X509 distinguished name of issuer. | always
&nbsp; | `subject` - X509 distinguished name of certificate subject. | always
&nbsp; | `serial_number` - Serial number of the issued certificate. (int) | always
&nbsp; | `revoked` - Revoked status of the certificate. (bool) | if certificate was revoked
&nbsp; | `owner_user` - The username that owns the certificate. | if `state: retrieved` and certificate is owned by a user
&nbsp; | `owner_host` - The host that owns the certificate. | if `state: retrieved` and certificate is owned by a host
&nbsp; | `owner_service` - The service that owns the certificate. | if `state: retrieved` and certificate is owned by a service
&nbsp; | `valid_not_before` - Time when issued certificate becomes valid, in GeneralizedTime format (YYYYMMDDHHMMSSZ) | always
&nbsp; | `valid_not_after` - Time when issued certificate ceases to be valid, in GeneralizedTime format (YYYYMMDDHHMMSSZ) | always
Authors
=======
Sam Morris
Rafael Jeffman

View File

@@ -25,7 +25,7 @@ Requirements
------------
**Controller**
* Ansible version: 2.8+
* Ansible version: 2.15+
**Node**
* Supported FreeIPA version (see above)
@@ -145,7 +145,7 @@ Variable | Description | Required
`selinuxusermaporder` \| `ipaselinuxusermaporder`| Set ordered list in increasing priority of SELinux users | no
`selinuxusermapdefault`\| `ipaselinuxusermapdefault` | Set default SELinux user when no match is found in SELinux map rule | no
`pac_type` \| `ipakrbauthzdata` | set default types of PAC supported for services (choices: `MS-PAC`, `PAD`, `nfs:NONE`). Use `""` to clear this variable. | no
`user_auth_type` \| `ipauserauthtype` | set default types of supported user authentication (choices: `password`, `radius`, `otp`, `disabled`). Use `""` to clear this variable. | no
`user_auth_type` \| `ipauserauthtype` | set default types of supported user authentication (choices: `password`, `radius`, `otp`, `pkinit`, `hardened`, `idp`, `disabled`, `""`). An additional check ensures that only types can be used that are supported by the IPA version. Use `""` to clear this variable. | no
`domain_resolution_order` \| `ipadomainresolutionorder` | Set list of domains used for short name qualification | no
`ca_renewal_master_server` \| `ipacarenewalmasterserver`| Renewal master for IPA certificate authority. | no
`enable_sid` | New users and groups automatically get a SID assigned. Cannot be deactivated once activated. Requires IPA 4.9.8+. (bool) | no

View File

@@ -23,7 +23,7 @@ Requirements
------------
**Controller**
* Ansible version: 2.8+
* Ansible version: 2.15+
**Node**
* Supported FreeIPA version (see above)

View File

@@ -22,7 +22,7 @@ Requirements
------------
**Controller**
* Ansible version: 2.8+
* Ansible version: 2.15+
**Node**
* Supported FreeIPA version (see above)

View File

@@ -21,7 +21,7 @@ FreeIPA versions 4.4.0 and up are supported by the ipadnsforwardzone module.
Requirements
------------
**Controller**
* Ansible version: 2.8+
* Ansible version: 2.15+
**Node**
* Supported FreeIPA version (see above)

View File

@@ -22,7 +22,7 @@ Requirements
------------
**Controller**
* Ansible version: 2.8+
* Ansible version: 2.15+
**Node**
* Supported FreeIPA version (see above)

View File

@@ -23,7 +23,7 @@ Requirements
------------
**Controller**
* Ansible version: 2.8+
* Ansible version: 2.15+
**Node**
@@ -133,6 +133,22 @@ Example playbook to enable a zone:
state: enabled
```
Example playbook to allow per-zone privilege delegation:
```yaml
---
- name: Playbook to enable per-zone privilege delegation
hosts: ipaserver
become: true
tasks:
- name: Enable privilege delegation.
ipadnszone:
ipaadmin_password: SomeADMINpassword
name: testzone.local
permission: true
```
Example playbook to remove a zone:
```yaml
@@ -223,6 +239,7 @@ Variable | Description | Required
`ttl`| Time to live for records at zone apex | no
`default_ttl`| Time to live for records without explicit TTL definition | no
`nsec3param_rec`| NSEC3PARAM record for zone in format: hash_algorithm flags iterations salt | no
`permission` \| `managedby` | Set per-zone access delegation permission. | no
`skip_overlap_check`| Force DNS zone creation even if it will overlap with an existing zone | no
`skip_nameserver_check` | Force DNS zone creation even if nameserver is not resolvable | no
@@ -238,4 +255,6 @@ Variable | Description | Returned When
Authors
=======
Sergio Oliveira Campos
- Sergio Oliveira Campos
- Thomas Woerner
- Rafael Jeffman

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
--------
@@ -26,7 +29,7 @@ Requirements
------------
**Controller**
* Ansible version: 2.8+
* Ansible version: 2.15+
**Node**
* Supported FreeIPA version (see above)
@@ -71,6 +74,101 @@ 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 rename a group:
```yaml
---
- name: Playbook to rename a single group
hosts: ipaserver
become: false
gather_facts: false
tasks:
- name: Rename group appops to webops
ipagroup:
ipaadmin_password: SomeADMINpassword
name: appops
rename: webops
state: renamed
```
Several groups can also be renamed with a single task, as in the example playbook:
```yaml
---
- name: Playbook to rename multiple groups
hosts: ipaserver
become: false
gather_facts: false
tasks:
- name: Rename group1 to newgroup1 and group2 to newgroup2
ipagroup:
ipaadmin_password: SomeADMINpassword
groups:
- name: group1
rename: newgroup1
- name: group2
rename: newgroup2
state: renamed
```
Example playbook to add users to a group:
```yaml
@@ -112,11 +210,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 +225,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 +252,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 +284,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
@@ -167,11 +301,13 @@ Variable | Description | Required
`membermanager_group` | List of member manager groups assigned to this group. Only usable with IPA versions 4.8.4 and up. | no
`externalmember` \| `ipaexternalmember` \| `external_member`| List of members of a trusted domain in DOM\\name or name@domain form. | no
`idoverrideuser` | List of user ID overrides to manage. Only usable with IPA versions 4.8.7 and up.| no
`rename` \| `new_name` | Rename the user object to the new name string. Only usable with `state: renamed`. | no
`action` | Work on group or member level. It can be on of `member` or `group` and defaults to `group`. | no
`state` | The state to ensure. It can be one of `present` or `absent`, default: `present`. | yes
`state` | The state to ensure. It can be one of `present`, `absent` or `renamed`, default: `present`. | yes
Authors
=======
Thomas Woerner
- Thomas Woerner
- Rafael Jeffman

View File

@@ -22,7 +22,7 @@ Requirements
------------
**Controller**
* Ansible version: 2.8+
* Ansible version: 2.15+
**Node**
* Supported FreeIPA version (see above)

View File

@@ -22,7 +22,7 @@ Requirements
------------
**Controller**
* Ansible version: 2.8+
* Ansible version: 2.15+
**Node**
* Supported FreeIPA version (see above)

View File

@@ -22,7 +22,7 @@ Requirements
------------
**Controller**
* Ansible version: 2.8+
* Ansible version: 2.15+
**Node**
* Supported FreeIPA version (see above)

View File

@@ -24,7 +24,7 @@ Requirements
------------
**Controller**
* Ansible version: 2.8+
* Ansible version: 2.15+
**Node**
* Supported FreeIPA version (see above)
@@ -335,7 +335,7 @@ Variable | Description | Required
-------- | ----------- | --------
`description` | The host description. | no
`locality` | Host locality (e.g. "Baltimore, MD"). | no
`location` \| `ns_host_location` | Host location (e.g. "Lab 2"). | no
`location` \| `ns_host_location` | Host physical location hint (e.g. "Lab 2"). | no
`platform` \| `ns_hardware_platform` | Host hardware platform (e.g. "Lenovo T61"). | no
`os` \| `ns_os_version` | Host operating system and version (e.g. "Fedora 9"). | no
`password` \| `user_password` \| `userpassword` | Password used in bulk enrollment for absent or not enrolled hosts. | no
@@ -354,7 +354,7 @@ Variable | Description | Required
`mac_address` \| `macaddress` | List of hardware MAC addresses. | no
`sshpubkey` \| `ipasshpubkey` | List of SSH public keys | no
`userclass` \| `class` | Host category (semantics placed on this attribute are for local interpretation) | no
`auth_ind` \| `krbprincipalauthind` | Defines an allow list for Authentication Indicators. Use 'otp' to allow OTP-based 2FA authentications. Use 'radius' to allow RADIUS-based 2FA authentications. Use empty string to reset auth_ind to the initial value. Other values may be used for custom configurations. choices: ["radius", "otp", "pkinit", "hardened", ""] | no
`auth_ind` \| `krbprincipalauthind` | Defines an allow list for Authentication Indicators. Use 'otp' to allow OTP-based 2FA authentications. Use 'radius' to allow RADIUS-based 2FA authentications. Use empty string to reset auth_ind to the initial value. Other values may be used for custom configurations. An additional check ensures that only types can be used that are supported by the IPA version. Choices: ["radius", "otp", "pkinit", "hardened", "idp", ""] | no
`requires_pre_auth` \| `ipakrbrequirespreauth` | Pre-authentication is required for the service (bool) | no
`ok_as_delegate` \| `ipakrbokasdelegate` | Client credentials may be delegated to the service (bool) | no
`ok_to_auth_as_delegate` \| `ipakrboktoauthasdelegate` | The service is allowed to authenticate on behalf of a client (bool) | no
@@ -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

@@ -26,7 +26,7 @@ Requirements
------------
**Controller**
* Ansible version: 2.8+
* Ansible version: 2.15+
**Node**
* Supported FreeIPA version (see above)

233
README-idoverridegroup.md Normal file
View File

@@ -0,0 +1,233 @@
Idoverridegroup module
============
Description
-----------
The idoverridegroup module allows to ensure presence and absence of idoverridegroups and idoverridegroup members.
Use Cases
---------
With idoverridegroup it is possible to manage group attributes within ID views. These attributes are for example the group name or gid.
Features
--------
* Idoverridegroup management
Supported FreeIPA Versions
--------------------------
FreeIPA versions 4.4.0 and up are supported by the ipaidoverridegroup module.
Requirements
------------
**Controller**
* Ansible version: 2.15+
**Node**
* Supported FreeIPA version (see above)
Usage
=====
Example inventory file
```ini
[ipaserver]
ipaserver.test.local
```
Example playbook to make sure test group test_group is present in idview test_idview
```yaml
---
- name: Playbook to manage idoverridegroup
hosts: ipaserver
become: false
tasks:
- name: Ensure test group test_group is present in idview test_idview.
ipaidoverridegroup:
ipaadmin_password: SomeADMINpassword
idview: test_idview
anchor: test_group
```
Example playbook to make sure test group test_group is present in idview test_idview with description
```yaml
---
- name: Playbook to manage idoverridegroup
hosts: ipaserver
become: false
tasks:
- name: Ensure test group test_group is present in idview test_idview with description
ipaidoverridegroup:
ipaadmin_password: SomeADMINpassword
idview: test_idview
anchor: test_group
description: "test_group description"
```
Example playbook to make sure test group test_group is present in idview test_idview without description
```yaml
---
- name: Playbook to manage idoverridegroup
hosts: ipaserver
become: false
tasks:
- name: Ensure test group test_group is present in idview test_idview without description
ipaidoverridegroup:
ipaadmin_password: SomeADMINpassword
idview: test_idview
anchor: test_group
description: ""
```
Example playbook to make sure test group test_group is present in idview test_idview with internal name test_123_group
```yaml
---
- name: Playbook to manage idoverridegroup
hosts: ipaserver
become: false
tasks:
- name: Ensure test group test_group is present in idview test_idview with internal name test_123_group
ipaidoverridegroup:
ipaadmin_password: SomeADMINpassword
idview: test_idview
anchor: test_group
name: test_123_group
```
Example playbook to make sure test group test_group is present in idview test_idview without internal name
```yaml
---
- name: Playbook to manage idoverridegroup
- name: Ensure test group test_group is present in idview test_idview without internal name
hosts: ipaserver
become: false
tasks:
- ipaidoverridegroup:
ipaadmin_password: SomeADMINpassword
idview: test_idview
anchor: test_group
name: ""
```
Example playbook to make sure test group test_group is present in idview test_idview with gid 20001
```yaml
---
- name: Playbook to manage idoverridegroup
hosts: ipaserver
become: false
tasks:
- name: Ensure test group test_group is present in idview test_idview with gid 20001
ipaidoverridegroup:
ipaadmin_password: SomeADMINpassword
idview: test_idview
anchor: test_group
gid: 20001
```
Example playbook to make sure test group test_group is present in idview test_idview without gid
```yaml
---
- name: Playbook to manage idoverridegroup
hosts: ipaserver
become: false
tasks:
- name: Ensure test group test_group is present in idview test_idview without gid
ipaidoverridegroup:
ipaadmin_password: SomeADMINpassword
idview: test_idview
anchor: test_group
gid: ""
```
Example playbook to make sure test group test_group is present in idview test_idview with enabling falling back to AD DC LDAP when resolving AD trusted objects. (For two-way trusts only.)
```yaml
---
- name: Playbook to manage idoverridegroup
hosts: ipaserver
become: false
tasks:
- name: Ensure test group test_group is present in idview test_idview with fallback_to_ldap enabled
ipaidoverridegroup:
ipaadmin_password: SomeADMINpassword
idview: test_idview
anchor: test_group
fallback_to_ldap: true
```
Example playbook to make sure test group test_group is absent in idview test_idview
```yaml
---
- name: Playbook to manage idoverridegroup
hosts: ipaserver
become: false
tasks:
- name: Ensure test group test_group is absent in idview test_idview
ipaidoverridegroup:
ipaadmin_password: SomeADMINpassword
idview: test_idview
anchor: test_group
continue: true
state: absent
```
Variables
---------
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 true. (bool) | no
`idview` \| `idviewcn` | The doverridegroup idview string. | yes
`anchor` \| `ipaanchoruuid` | The list of anchors to override. | yes
`description` \| `desc` | Description | no
`name` \| `group_name` \| `cn` | The group. | no
`gid` \| `gidnumber` | Group ID Number (int or "") | no
`fallback_to_ldap` | Allow falling back to AD DC LDAP when resolving AD trusted objects. For two-way trusts only. | no
`delete_continue` \| `continue` | Continuous mode. Don't stop on errors. Valid only if `state` is `absent`. | no
`state` | The state to ensure. It can be one of `present`, `absent`, default: `present`. | no
Authors
=======
Thomas Woerner

503
README-idoverrideuser.md Normal file
View File

@@ -0,0 +1,503 @@
Idoverrideuser module
============
Description
-----------
The idoverrideuser module allows to ensure presence and absence of idoverrideusers and idoverrideuser members.
Use Cases
---------
With idoverrideuser it is possible to manage user attributes within ID views. These attributes are for example the login name, home directory, certificate for authentication or SSH keys.
Features
--------
* Idoverrideuser management
Supported FreeIPA Versions
--------------------------
FreeIPA versions 4.4.0 and up are supported by the ipaidoverrideuser module.
Requirements
------------
**Controller**
* Ansible version: 2.15+
**Node**
* Supported FreeIPA version (see above)
Usage
=====
Example inventory file
```ini
[ipaserver]
ipaserver.test.local
```
Example playbook to make sure test user test_user is present in idview test_idview
```yaml
---
- name: Playbook to manage idoverrideuser
hosts: ipaserver
become: false
tasks:
- name: Ensure test user test_user is present in idview test_idview.
ipaidoverrideuser:
ipaadmin_password: SomeADMINpassword
idview: test_idview
anchor: test_user
```
Example playbook to make sure test user test_user is present in idview test_idview with description
```yaml
---
- name: Playbook to manage idoverrideuser
hosts: ipaserver
become: false
tasks:
- name: Ensure test user test_user is present in idview test_idview with description
ipaidoverrideuser:
ipaadmin_password: SomeADMINpassword
idview: test_idview
anchor: test_user
description: "test_user description"
```
Example playbook to make sure test user test_user is present in idview test_idview without description
```yaml
---
- name: Playbook to manage idoverrideuser
hosts: ipaserver
become: false
tasks:
- name: Ensure test user test_user is present in idview test_idview without description
ipaidoverrideuser:
ipaadmin_password: SomeADMINpassword
idview: test_idview
anchor: test_user
description: ""
```
Example playbook to make sure test user test_user is present in idview test_idview with internal name test_123_user
```yaml
---
- name: Playbook to manage idoverrideuser
hosts: ipaserver
become: false
tasks:
- name: Ensure test user test_user is present in idview test_idview with internal name test_123_user
ipaidoverrideuser:
ipaadmin_password: SomeADMINpassword
idview: test_idview
anchor: test_user
name: test_123_user
```
Example playbook to make sure test user test_user is present in idview test_idview without internal name
```yaml
---
- name: Playbook to manage idoverrideuser
hosts: ipaserver
become: false
tasks:
- name: Ensure test user test_user is present in idview test_idview without internal name
ipaidoverrideuser:
ipaadmin_password: SomeADMINpassword
idview: test_idview
anchor: test_user
name: ""
```
Example playbook to make sure test user test_user is present in idview test_idview with uid 20001
```yaml
---
- name: Playbook to manage idoverrideuser
hosts: ipaserver
become: false
tasks:
- name: Ensure test user test_user is present in idview test_idview with uid 20001
ipaidoverrideuser:
ipaadmin_password: SomeADMINpassword
idview: test_idview
anchor: test_user
uid: 20001
```
Example playbook to make sure test user test_user is present in idview test_idview without uid
```yaml
---
- name: Playbook to manage idoverrideuser
hosts: ipaserver
become: false
tasks:
- name: Ensure test user test_user is present in idview test_idview without uid
ipaidoverrideuser:
ipaadmin_password: SomeADMINpassword
idview: test_idview
anchor: test_user
uid: ""
```
Example playbook to make sure test user test_user is present in idview test_idview with gecos "Gecos Test"
```yaml
---
- name: Playbook to manage idoverrideuser
hosts: ipaserver
become: false
tasks:
- name: Ensure test user test_user is present in idview test_idview with gecos "Gecos Test"
ipaidoverrideuser:
ipaadmin_password: SomeADMINpassword
idview: test_idview
anchor: test_user
gecos: Gecos Test
```
Example playbook to make sure test user test_user is present in idview test_idview without gecos
```yaml
---
- name: Playbook to manage idoverrideuser
hosts: ipaserver
become: false
tasks:
- name: Ensure test user test_user is present in idview test_idview without gecos
ipaidoverrideuser:
ipaadmin_password: SomeADMINpassword
idview: test_idview
anchor: test_user
gecos: ""
```
Example playbook to make sure test user test_user is present in idview test_idview with gidnumber
```yaml
---
- name: Playbook to manage idoverrideuser
hosts: ipaserver
become: false
tasks:
- name: Ensure test user test_user is present in idview test_idview with gidnumber
ipaidoverrideuser:
ipaadmin_password: SomeADMINpassword
idview: test_idview
anchor: test_user
gidnumber: 20001
```
Example playbook to make sure test user test_user is present in idview test_idview without gidnumber
```yaml
---
- name: Playbook to manage idoverrideuser
hosts: ipaserver
become: false
tasks:
- name: Ensure test user test_user is present in idview test_idview without gidnumber
ipaidoverrideuser:
ipaadmin_password: SomeADMINpassword
idview: test_idview
anchor: test_user
gidnumber: ""
```
Example playbook to make sure test user test_user is present in idview test_idview with homedir /Users
```yaml
---
- name: Playbook to manage idoverrideuser
hosts: ipaserver
become: false
tasks:
- name: Ensure test user test_user is present in idview test_idview with homedir /Users
ipaidoverrideuser:
ipaadmin_password: SomeADMINpassword
idview: test_idview
anchor: test_user
homedir: /Users
```
Example playbook to make sure test user test_user is present in idview test_idview without homedir
```yaml
---
- name: Playbook to manage idoverrideuser
hosts: ipaserver
become: false
tasks:
- name: Ensure test user test_user is present in idview test_idview without homedir
ipaidoverrideuser:
ipaadmin_password: SomeADMINpassword
idview: test_idview
anchor: test_user
homedir: ""
```
Example playbook to make sure test user test_user is present in idview test_idview with shell
```yaml
---
- name: Playbook to manage idoverrideuser
hosts: ipaserver
become: false
tasks:
- name: Ensure test user test_user is present in idview test_idview with shell
ipaidoverrideuser:
ipaadmin_password: SomeADMINpassword
idview: test_idview
anchor: test_user
shell: /bin/someshell
```
Example playbook to make sure test user test_user is present in idview test_idview without shell
```yaml
---
- name: Playbook to manage idoverrideuser
hosts: ipaserver
become: false
tasks:
- name: Ensure test user test_user is present in idview test_idview without shell
ipaidoverrideuser:
ipaadmin_password: SomeADMINpassword
idview: test_idview
anchor: test_user
shell: ""
```
Example playbook to make sure test user test_user is present in idview test_idview with sshpubkey
```yaml
---
- name: Playbook to manage idoverrideuser
hosts: ipaserver
become: false
tasks:
- name: Ensure test user test_user is present in idview test_idview with sshpubkey
ipaidoverrideuser:
ipaadmin_password: SomeADMINpassword
idview: test_idview
anchor: test_user
sshpubkey:
- ssh-rsa AAAAB3NzaC1yc2EAAADAQABAAABgQCqmVDpEX5gnSjKuv97Ay ...
```
Example playbook to make sure test user test_user is present in idview test_idview without sshpubkey
```yaml
---
- name: Playbook to manage idoverrideuser
hosts: ipaserver
become: false
tasks:
- name: Ensure test user test_user is present in idview test_idview without sshpubkey
ipaidoverrideuser:
ipaadmin_password: SomeADMINpassword
idview: test_idview
anchor: test_user
sshpubkey: []
```
Example playbook to make sure test user test_user is present in idview test_idview with 1 certificate
```yaml
---
- name: Playbook to manage idoverrideuser
hosts: ipaserver
become: false
tasks:
- name: Ensure test user test_user is present in idview test_idview with 1 certificate
ipaidoverrideuser:
ipaadmin_password: SomeADMINpassword
idview: test_idview
anchor: test_user
certificate:
- "{{ lookup('file', 'cert1.b64', rstrip=False) }}"
```
Example playbook to make sure test user test_user is present in idview test_idview with 3 certificate members
```yaml
---
- name: Playbook to manage idoverrideuser
hosts: ipaserver
become: false
tasks:
- name: Ensure test user test_user is present in idview test_idview with 3 certificate members
ipaidoverrideuser:
ipaadmin_password: SomeADMINpassword
idview: test_idview
anchor: test_user
certificate:
- "{{ lookup('file', 'cert1.b64', rstrip=False) }}"
- "{{ lookup('file', 'cert2.b64', rstrip=False) }}"
- "{{ lookup('file', 'cert3.b64', rstrip=False) }}"
action: member
```
Example playbook to make sure test user test_user is present in idview test_idview without 2 certificate members
```yaml
---
- name: Playbook to manage idoverrideuser
hosts: ipaserver
become: false
tasks:
- name: Ensure test user test_user is present in idview test_idview without 2 certificate members
ipaidoverrideuser:
ipaadmin_password: SomeADMINpassword
idview: test_idview
anchor: test_user
certificate:
- "{{ lookup('file', 'cert2.b64', rstrip=False) }}"
- "{{ lookup('file', 'cert3.b64', rstrip=False) }}"
action: member
state: absent
```
Example playbook to make sure test user test_user is present in idview test_idview without certificates
```yaml
---
- name: Playbook to manage idoverrideuser
hosts: ipaserver
become: false
tasks:
- name: Ensure test user test_user is present in idview test_idview without certificates
ipaidoverrideuser:
ipaadmin_password: SomeADMINpassword
idview: test_idview
anchor: test_user
certificate: []
```
Example playbook to make sure test user test_user is present in idview test_idview with enabling falling back to AD DC LDAP when resolving AD trusted objects. (For two-way trusts only.)
```yaml
---
- name: Playbook to manage idoverrideuser
hosts: ipaserver
become: false
tasks:
- name: Ensure test user test_user is present in idview test_idview with fallback_to_ldap enabled
ipaidoverrideuser:
ipaadmin_password: SomeADMINpassword
idview: test_idview
anchor: test_user
fallback_to_ldap: true
```
Example playbook to make sure test user test_user is absent in idview test_idview
```yaml
---
- name: Playbook to manage idoverrideuser
hosts: ipaserver
become: false
tasks:
- name: Ensure test user test_user is absent in idview test_idview
ipaidoverrideuser:
ipaadmin_password: SomeADMINpassword
idview: test_idview
anchor: test_user
continue: true
state: absent
```
Variables
---------
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 true. (bool) | no
`idview` \| `idviewcn` | The doverrideuser idview string. | yes
`anchor` \| `ipaanchoruuid` | The list of anchors to override. | yes
`description` \| `desc` | Description | no
`name` \| `login` | The user (internally uid) | no
`uid` \| `uidnumber` | User ID Number (int or "") | no
`gecos` | GECOS | no
`gidnumber` | Group ID Number (int or ""). | no
`homedir` \| `homedirectory` | Home directory. | no
`shell` \| `loginshell` | Login shell. | no
`sshpubkey` \| `ipasshpubkey` | List of SSH public keys. | no
`certificate` \| `usercertificate` | List of Base-64 encoded user certificates. This variable can also be used with `action: member`. | no
`fallback_to_ldap` | Allow falling back to AD DC LDAP when resolving AD trusted objects. For two-way trusts only. | no
`delete_continue` \| `continue` | Continuous mode. Don't stop on errors. Valid only if `state` is `absent`. | no
`nomembers` \| `no_members` | Suppress processing of membership attributes. Valid only if `state` is `absent`. | no
`action` | Work on idoverrideuser or member level. It can be on of `member` or `idoverrideuser` and defaults to `idoverrideuser`. | no
`state` | The state to ensure. It can be one of `present`, `absent`, default: `present`. | no
Authors
=======
Thomas Woerner

192
README-idp.md Normal file
View File

@@ -0,0 +1,192 @@
Idp module
============
Description
-----------
The idp module allows to ensure presence and absence of idps.
Features
--------
* Idp management
Supported FreeIPA Versions
--------------------------
FreeIPA versions 4.4.0 and up are supported by the ipaidp module.
Requirements
------------
**Controller**
* Ansible version: 2.15+
**Node**
* Supported FreeIPA version (see above)
Usage
=====
Example inventory file
```ini
[ipaserver]
ipaserver.test.local
```
Example playbook to make sure keycloak idp my-keycloak-idp is present:
```yaml
---
- name: Playbook to manage IPA idp.
hosts: ipaserver
become: false
tasks:
- name: Ensure keycloak idp my-keycloak-idp is present
ipaidp:
ipaadmin_password: SomeADMINpassword
name: my-keycloak-idp
provider: keycloak
organization: main
base_url: keycloak.idm.example.com:8443/auth
client_id: my-client-id
```
Example playbook to make sure keycloak idp my-keycloak-idp is absent:
```yaml
---
- name: Playbook to manage IPA idp.
hosts: ipaserver
become: false
tasks:
- name: Ensure keycloak idp my-keycloak-idp is absent
ipaidp:
ipaadmin_password: SomeADMINpassword
name: my-keycloak-idp
delete_continue: true
state: absent
```
Example playbook to make sure github idp my-github-idp is present:
```yaml
---
- name: Playbook to manage IPA idp.
hosts: ipaserver
become: false
tasks:
- name: Ensure github idp my-github-idp is present
ipaidp:
ipaadmin_password: SomeADMINpassword
name: my-github-idp
provider: github
client_id: my-github-client-id
```
Example playbook to make sure google idp my-google-idp is present using provider defaults without specifying provider:
```yaml
---
- name: Playbook to manage IPA idp.
hosts: ipaserver
become: false
tasks:
- name: Ensure google idp my-google-idp is present using provider defaults without specifying provider
ipaidp:
ipaadmin_password: SomeADMINpassword
name: my-google-idp
auth_uri: https://accounts.google.com/o/oauth2/auth
dev_auth_uri: https://oauth2.googleapis.com/device/code
token_uri: https://oauth2.googleapis.com/token
keys_uri: https://www.googleapis.com/oauth2/v3/certs
userinfo_uri: https://openidconnect.googleapis.com/v1/userinfo
client_id: my-google-client-id
scope: "openid email"
idp_user_id: email
```
Example playbook to make sure google idp my-google-idp is present using provider:
```yaml
---
- name: Playbook to manage IPA idp.
hosts: ipaserver
become: false
tasks:
- name: Ensure google idp my-google-idp is present using provider
ipaidp:
ipaadmin_password: SomeADMINpassword
name: my-google-idp
provider: google
client_id: my-google-client-id
```
Example playbook to make sure idps my-keycloak-idp, my-github-idp and my-google-idp are absent:
```yaml
---
- name: Playbook to manage IPA idp.
hosts: ipaserver
become: false
tasks:
- name: Ensure idps my-keycloak-idp, my-github-idp and my-google-idp are absent
ipaidp:
ipaadmin_password: SomeADMINpassword
name:
- my-keycloak-idp
- my-github-idp
- my-google-idp
delete_continue: true
state: absent
```
Variables
---------
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 true. (bool) | false
`name` \| `cn` | The list of idp name strings. | yes
auth_uri \| ipaidpauthendpoint | OAuth 2.0 authorization endpoint string. | no
dev_auth_uri \| ipaidpdevauthendpoint | Device authorization endpoint string. | no
token_uri \| ipaidptokenendpoint | Token endpoint string. | no
userinfo_uri \| ipaidpuserinfoendpoint | User information endpoint string. | no
keys_uri \| ipaidpkeysendpoint | JWKS endpoint string. | no
issuer_url \| ipaidpissuerurl | The Identity Provider OIDC URL string. | no
client_id \| ipaidpclientid | OAuth 2.0 client identifier string. | no
secret \| ipaidpclientsecret | OAuth 2.0 client secret string. | no
scope \| ipaidpscope | OAuth 2.0 scope string. Multiple scopes separated by space. | no
idp_user_id \| ipaidpsub | Attribute string for user identity in OAuth 2.0 userinfo. | no
provider \| ipaidpprovider | Pre-defined template string. This provides the provider defaults, which can be overridden with the other IdP options. Choices: ["google","github","microsoft","okta","keycloak"] | no
organization \| ipaidporg | Organization ID string or Realm name for IdP provider templates. | no
base_url \| ipaidpbaseurl | Base URL string for IdP provider templates. | no
rename \| new_name | New name for the Identity Provider server object. Only with `state: renamed`. | no
delete_continue \| continue | Continuous mode. Don't stop on errors. Valid only if `state` is `absent`. | no
`state` | The state to ensure. It can be one of `present`, `absent`, `renamed`, default: `present`. | no
Authors
=======
Thomas Woerner

View File

@@ -37,7 +37,7 @@ Requirements
------------
**Controller**
* Ansible version: 2.8+
* Ansible version: 2.15+
**Node**
* Supported FreeIPA version (see above)

153
README-idview.md Normal file
View File

@@ -0,0 +1,153 @@
Idview module
============
Description
-----------
The idview module allows to ensure presence and absence of idviews and idview host members.
Use Cases
---------
With ID views it is possible to override user or group attributes for users stored in the LDAP server. For example the login name, home directory, certificate for authentication or SSH keys. An ID view is client-side and specifies new values for user or group attributes and also the client host or hosts on which the values apply.
The ID view and the applied hosts are managed with idview, the user attributes are managed with idoverrideuser and the group attributes with idoverridegroup.
Features
--------
* Idview management
Supported FreeIPA Versions
--------------------------
FreeIPA versions 4.4.0 and up are supported by the ipaidview module.
Requirements
------------
**Controller**
* Ansible version: 2.15+
**Node**
* Supported FreeIPA version (see above)
Usage
=====
Example inventory file
```ini
[ipaserver]
ipaserver.test.local
```
Example playbook to make sure idview "test_idview" is present:
```yaml
---
- name: Playbook to manage IPA idview.
hosts: ipaserver
become: false
tasks:
- ipaidview:
ipaadmin_password: SomeADMINpassword
name: test_idview
```
Example playbook to make sure idview "test_idview" member host "testhost.example.com" is present:
```yaml
---
- name: Playbook to manage IPA idview host member.
hosts: ipaserver
become: false
tasks:
- ipaidview:
ipaadmin_password: SomeADMINpassword
name: test_idview
host: testhost.example.com
action: member
```
Example playbook to make sure idview "test_idview" member host "testhost.example.com" is absent:
```yaml
---
- name: Playbook to manage IPA idview host member.
hosts: ipaserver
become: false
tasks:
- ipaidview:
ipaadmin_password: SomeADMINpassword
name: test_idview
host: testhost.example.com
action: member
state: absent
```
Example playbook to make sure idview "test_idview" is present with domain_resolution_order for "ad.example.com:ipa.example.com":
```yaml
---
- name: Playbook to manage IPA idview host member.
hosts: ipaserver
become: false
tasks:
- ipaidview:
ipaadmin_password: SomeADMINpassword
name: test_idview
domain_resolution_order: "ad.example.com:ipa.example.com"
```
Example playbook to make sure idview "test_idview" is absent:
```yaml
---
- name: Playbook to manage IPA idview.
hosts: ipaserver
become: false
tasks:
- ipaidview:
ipaadmin_password: SomeADMINpassword
name: test_idview
state: absent
```
Variables
---------
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 true. (bool) | no
`name` \| `cn` | The list of idview name strings. | yes
`description` \| `desc` | The description string of the idview. | no
`domain_resolution_order` \| `ipadomainresolutionorder` | Colon-separated list of domains used for short name qualification. | no
`host` \| `hosts` | List of hosts to apply the ID View to. A host can only be applied to a single idview at any time. Applying a host that is already applied to a different idview will change the idview the host is applied to to the new one. | no
`rename` \| `new_name` | Rename the ID view object to the new name string. Only usable with `state: renamed`. | no
`delete_continue` \| `continue` | Continuous mode. Don't stop on errors. Valid only if `state` is `absent`. | no
`action` | Work on idview or member level. It can be on of `member` or `idview` and defaults to `idview`. | no
`state` | The state to ensure. It can be one of `present`, `absent` and `renamed`, default: `present`. | no
Authors
=======
Thomas Woerner

View File

@@ -0,0 +1,106 @@
Inventory plugin
================
Description
-----------
The inventory plugin compiles a dynamic inventory from IPA domain. The servers can be filtered by their role(s).
This plugin is using the Python requests binding, that is only available for Python 3.7 and up.
Features
--------
* Dynamic inventory
Supported FreeIPA Versions
--------------------------
FreeIPA versions 4.6.0 and up are supported by the inventory plugin.
Requirements
------------
**Controller**
* Ansible version: 2.15+
**Node**
* Supported FreeIPA version (see above)
Configuration
=============
The inventory plugin is automatically enabled from the Ansible collection or from the top directory of the git repo if the `plugins` folder is linked to `~/.ansible`.
If `ansible.cfg` was modified to point to the roles and modules with `roles_path`, `library` and `module_utils` tag, then it is needed to set `inventory_plugins` also:
```
inventory_plugins = /my/dir/ansible-freeipa/plugins/inventory
```
Usage
=====
Example inventory file "freeipa.yml":
```yml
---
plugin: freeipa
server: server.ipa.local
ipaadmin_password: SomeADMINpassword
```
Example inventory file "freeipa.yml" with server TLS certificate verification using local copy of `/etc/ipa/ca.crt` from the server:
```yml
---
plugin: freeipa
server: server.ipa.local
ipaadmin_password: SomeADMINpassword
verify: ca.crt
```
How to use the plugin
---------------------
With the `ansible-inventory` command it is possible to show the generated inventorey:
```bash
ansible-inventory -v -i freeipa.yml --graph
```
Example inventory file "freeipa.yml" for use with `playbooks/config/retrieve-config.yml`:
```yml
---
plugin: freeipa
server: server.ipa.local
ipaadmin_password: SomeADMINpassword
inventory_group: ipaserver
```
```bash
ansible-playbook -u root -i ipa.yml playbooks/config/retrieve-config.yml
```
Variables
=========
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
`server` | The FQDN of server to start the scan. (string) | yes
`verify` | The server TLS certificate file for verification (/etc/ipa/ca.crt). Turned off if not set. (string) | yes
`role` | The role(s) of the server. If several roles are given, only servers that have all the roles are returned. (list of strings) (choices: "IPA master", "CA server", "KRA server", "DNS server", "AD trust controller", "AD trust agent") | no
`inventory_group` | The inventory group to create. The default group name is "ipaservers". | no
Authors
=======
- Thomas Woerner

View File

@@ -22,7 +22,7 @@ Requirements
------------
**Controller**
* Ansible version: 2.8+
* Ansible version: 2.15+
**Node**
* Supported FreeIPA version (see above)

View File

@@ -22,7 +22,7 @@ Requirements
------------
**Controller**
* Ansible version: 2.8+
* Ansible version: 2.15+
**Node**
* Supported FreeIPA version (see above)

View File

@@ -22,7 +22,7 @@ Requirements
------------
**Controller**
* Ansible version: 2.8+
* Ansible version: 2.15+
**Node**
* Supported FreeIPA version (see above)

View File

@@ -22,7 +22,7 @@ Requirements
------------
**Controller**
* Ansible version: 2.8+
* Ansible version: 2.15+
**Node**
* Supported FreeIPA version (see above)

View File

@@ -22,7 +22,7 @@ Requirements
------------
**Controller**
* Ansible version: 2.8+
* Ansible version: 2.15+
**Node**
* Supported FreeIPA version (see above)
@@ -128,20 +128,20 @@ Variable | Description | Required
`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
`name` \| `cn` | The list of pwpolicy name strings. If name is not given, `global_policy` will be used automatically. | no
`maxlife` \| `krbmaxpwdlife` | Maximum password lifetime in days. (int) | no
`minlife` \| `krbminpwdlife` | Minimum password lifetime in hours. (int) | no
`history` \| `krbpwdhistorylength` | Password history size. (int) | no
`minclasses` \| `krbpwdmindiffchars` | Minimum number of character classes. (int) | no
`minlength` \| `krbpwdminlength` | Minimum length of password. (int) | no
`priority` \| `cospriority` | Priority of the policy, higher number means lower priority. (int) | no
`maxfail` \| `krbpwdmaxfailure` | Consecutive failures before lockout. (int) | no
`failinterval` \| `krbpwdfailurecountinterval` | Period after which failure count will be reset in seconds. (int) | no
`lockouttime` \| `krbpwdlockoutduration` | Period for which lockout is enforced in seconds. (int) | no
`maxrepeat` \| `ipapwdmaxrepeat` | Maximum number of same consecutive characters. Requires IPA 4.9+ (int) | no
`maxsequence` \| `ipapwdmaxsequence` | The maximum length of monotonic character sequences (abcd). Requires IPA 4.9+ (int) | no
`dictcheck` \| `ipapwdictcheck` | Check if the password is a dictionary word. Requires IPA 4.9+ (int) | no
`usercheck` \| `ipapwdusercheck` | Check if the password contains the username. Requires IPA 4.9+ (int) | no
`gracelimit` \| `passwordgracelimit` | Number of LDAP authentications allowed after expiration. Requires IPA 4.9.10 (int) | no
`maxlife` \| `krbmaxpwdlife` | Maximum password lifetime in days. (int or "") | no
`minlife` \| `krbminpwdlife` | Minimum password lifetime in hours. (int or "") | no
`history` \| `krbpwdhistorylength` | Password history size. (int or "") | no
`minclasses` \| `krbpwdmindiffchars` | Minimum number of character classes. (int or "") | no
`minlength` \| `krbpwdminlength` | Minimum length of password. (int or "") | no
`priority` \| `cospriority` | Priority of the policy, higher number means lower priority. (int or "") | no
`maxfail` \| `krbpwdmaxfailure` | Consecutive failures before lockout. (int or "") | no
`failinterval` \| `krbpwdfailurecountinterval` | Period after which failure count will be reset in seconds. (int or "") | no
`lockouttime` \| `krbpwdlockoutduration` | Period for which lockout is enforced in seconds. (int or "") | no
`maxrepeat` \| `ipapwdmaxrepeat` | Maximum number of same consecutive characters. Requires IPA 4.9+ (int or "") | no
`maxsequence` \| `ipapwdmaxsequence` | The maximum length of monotonic character sequences (abcd). Requires IPA 4.9+ (int or "") | no
`dictcheck` \| `ipapwdictcheck` | Check if the password is a dictionary word. Requires IPA 4.9+. (bool or "") | no
`usercheck` \| `ipapwdusercheck` | Check if the password contains the username. Requires IPA 4.9+. (bool or "") | no
`gracelimit` \| `passwordgracelimit` | Number of LDAP authentications allowed after expiration. Requires IPA 4.9.10 (int or "") | no
`state` | The state to ensure. It can be one of `present` or `absent`, default: `present`. | yes

View File

@@ -25,7 +25,7 @@ Requirements
------------
**Controller**
* Ansible version: 2.8+
* Ansible version: 2.15+
**Node**
* Supported FreeIPA version (see above)

View File

@@ -23,7 +23,7 @@ Requirements
------------
**Controller**
* Ansible version: 2.8+
* Ansible version: 2.15+
**Node**
* Supported FreeIPA version (see above)

View File

@@ -22,7 +22,7 @@ Requirements
------------
**Controller**
* Ansible version: 2.8+
* Ansible version: 2.15+
**Node**
* Supported FreeIPA version (see above)
@@ -249,14 +249,14 @@ Variable | Description | Required
`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
`name` \| `cn` | The list of server name strings. | yes
`location` \| `ipalocation_location` | The server location string. Only in state: present. "" for location reset. | no
`service_weight` \| `ipaserviceweight` | Weight for server services. Type Values 0 to 65535, -1 for weight reset. Only in state: present. (int) | no
`hidden` | Set hidden state of a server. Only in state: present. (bool) | no
`no_members` | Suppress processing of membership attributes. Only in state: present. (bool) | no
`delete_continue` \| `continue` | Continuous mode: Don't stop on errors. Only in state: absent. (bool) | no
`ignore_last_of_role` | Skip a check whether the last CA master or DNS server is removed. Only in state: absent. (bool) | no
`ignore_topology_disconnect` | Ignore topology connectivity problems after removal. Only in state: absent. (bool) | no
`force` | Force server removal even if it does not exist. Will always result in changed. Only in state: absent. (bool) | no
`location` \| `ipalocation_location` | The server DNS location. Only available with 'state: present'. Use "" for location reset. | no
`service_weight` \| `ipaserviceweight` | Weight for server services. Type Values 0 to 65535, -1 for weight reset. Only available with 'state: present'. (int) | no
`hidden` | Set hidden state of a server. Only available with 'state: present'. (bool) | no
`no_members` | Suppress processing of membership attributes. Only avialable with 'state: present'. (bool) | no
`delete_continue` \| `continue` | Continuous mode: Don't stop on errors. Only available with 'state: absent'. (bool) | no
`ignore_last_of_role` | Skip a check whether the last CA master or DNS server is removed. Only available with 'state: absent'. (bool) | no
`ignore_topology_disconnect` | Ignore topology connectivity problems after removal. Only available with 'state: absent'. (bool) | no
`force` | Force server removal even if it does not exist. Will always result in changed. Only available with 'state: absent'. (bool) | no
`state` | The state to ensure. It can be one of `present`, `absent`, default: `present`. `present` is only working with existing servers. | no

View File

@@ -25,7 +25,7 @@ Requirements
------------
**Controller**
* Ansible version: 2.8+
* Ansible version: 2.15+
**Node**
* Supported FReeIPA version (see above)
@@ -282,6 +282,65 @@ Example playbook to allow users, groups, hosts or hostgroups to retrieve a keyta
```
Example playbook to ensure presence of serveral services in a single task:
```yaml
---
- name: Playbook to manage IPA service.
hosts: ipaserver
tasks:
- name: Ensure services are present
ipaservice:
ipaadmin_password: SomeADMINpassword
services:
- name: HTTP/www.example.com
principal:
- host/host1.example.com
- name: mysvc/www.example.com
pac_type: NONE
ok_as_delegate: yes
ok_to_auth_as_delegate: yes
- name: HTTP/www.example.com
allow_create_keytab_user:
- user01
- user02
allow_create_keytab_group:
- group01
- group02
allow_create_keytab_host:
- host1.example.com
- host2.example.com
allow_create_keytab_hostgroup:
- hostgroup01
- hostgroup02
- name: mysvc/host2.example.com
auth_ind: otp,radius
```
Example playbook to ensure presence of serveral services in a single task with `member` `action`:
```yaml
---
- name: Playbook to manage IPA service.
hosts: ipaserver
become: true
gather_facts: false
tasks:
- name: Ensure service host members are present
ipaservice:
ipaadmin_password: SomeADMINpassword
services:
- name: HTTP/www1.example.com
host: host1.example.com
- name: HTTP/www2.example.com
host: host2.example.com
action: member
```
Variables
---------
@@ -291,10 +350,18 @@ Variable | Description | Required
`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
`name` \| `service` | The list of service name strings. | yes
`name` \| `service` | The list of service name strings. `name` with *service variables* or `services` containing *service variables* need to be used. | no
`action` | Work on service or member level. It can be on of `member` or `service` and defaults to `service`. | no
`state` | The state to ensure. It can be one of `present`, `absent`, or `disabled`, default: `present`. | no
**Service Variables:**
Variable | Description | Required
-------- | ----------- | --------
`certificate` \| `usercertificate` | Base-64 encoded service certificate. | no
`pac_type` \| `ipakrbauthzdata` | Supported PAC type. It can be one of `MS-PAC`, `PAD`, or `NONE`. Use empty string to reset pac_type to the initial value. | no
`auth_ind` \| `krbprincipalauthind` | Defines an allow list for Authentication Indicators. It can be any of `otp`, `radius`, `pkinit` or `hardened`. Use empty string to reset auth_ind to the initial value. | no
`auth_ind` \| `krbprincipalauthind` | Defines an allow list for Authentication Indicators. It can be any of `otp`, `radius`, `pkinit`, `hardened`, `idp` or `""`. An additional check ensures that only types can be used that are supported by the IPA version. Use empty string to reset auth_ind to the initial value. | no
`requires_pre_auth` \| `ipakrbrequirespreauth` | Pre-authentication is required for the service. Default to true. (bool) | no
`ok_as_delegate` \| `ipakrbokasdelegate` | Client credentials may be delegated to the service. Default to false. (bool) | no
`ok_to_auth_as_delegate` \| `ipakrboktoauthasdelegate` | The service is allowed to authenticate on behalf of a client. Default to false. (bool) | no
@@ -310,11 +377,9 @@ Variable | Description | Required
`allow_retrieve_keytab_group` \| `ipaallowedtoperform_read_keys_group` | Groups allowed to retrieve a keytab of this host. | no
`allow_retrieve_keytab_host` \| `ipaallowedtoperform_read_keys_host` | Hosts allowed to retrieve a keytab from of host. | no
`allow_retrieve_keytab_hostgroup` \| `ipaallowedtoperform_read_keys_hostgroup` | Host groups allowed to retrieve a keytab of this host. | no
`continue` | Continuous mode: don't stop on errors. Valid only if `state` is `absent`. Default: `no` (bool) | no
`smb` | Service is an SMB service. If set, `cifs/` will be prefixed to the service name if needed. | no
`netbiosname` | NETBIOS name for the SMB service. Only with `smb: yes`. | no
`action` | Work on service or member level. It can be on of `member` or `service` and defaults to `service`. | no
`state` | The state to ensure. It can be one of `present`, `absent`, or `disabled`, default: `present`. | no
`continue` \| `delete_continue` | Continuous mode: don't stop on errors. Valid only if `state` is `absent`. Default: `no` (bool) | no
Authors

View File

@@ -24,7 +24,7 @@ Requirements
------------
**Controller**
* Ansible version: 2.8+
* Ansible version: 2.15+
**Node**
* Supported FreeIPA version (see above)

View File

@@ -24,7 +24,7 @@ Requirements
------------
**Controller**
* Ansible version: 2.8+
* Ansible version: 2.15+
**Node**
* Supported FreeIPA version (see above)

View File

@@ -24,7 +24,7 @@ Requirements
------------
**Controller**
* Ansible version: 2.8+
* Ansible version: 2.15+
**Node**
* Supported FreeIPA version (see above)

View File

@@ -24,7 +24,7 @@ Requirements
------------
**Controller**
* Ansible version: 2.8+
* Ansible version: 2.15+
**Node**
* Supported FreeIPA version (see above)

View File

@@ -22,7 +22,7 @@ Requirements
------------
**Controller**
* Ansible version: 2.8+
* Ansible version: 2.15+
**Node**
* Supported FreeIPA version (see above)
@@ -93,6 +93,26 @@ Example playbook to make sure sudocmds are not present in Sudo Rule:
state: absent
```
Example playbook to ensure a Group of RunAs User is present in sudo rule:
```yaml
---
- name: Playbook to manage sudorule member
hosts: ipaserver
become: no
gather_facts: no
tasks:
- name: Ensure sudorule 'runasuser' has 'ipasuers' group as runas users.
ipasudorule:
ipaadmin_password: SomeADMINpassword
name: testrule1
runasuser_group: ipausers
action: member
```
Example playbook to make sure Sudo Rule is absent:
```yaml

View File

@@ -22,7 +22,7 @@ Requirements
------------
**Controller**
* Ansible version: 2.8+
* Ansible version: 2.15+
**Node**
* Supported FreeIPA version (see above)

View File

@@ -21,7 +21,7 @@ Requirements
**Controller**
* Ansible version: 2.8+
* Ansible version: 2.15+
**Node**

View File

@@ -24,7 +24,7 @@ Requirements
------------
**Controller**
* Ansible version: 2.8+
* Ansible version: 2.15+
**Node**
* Supported FreeIPA version (see above)
@@ -58,6 +58,7 @@ Example playbook to ensure a user is present:
last: Acme
uid: 10001
gid: 100
gecos: "The Pinky"
phone: "+555123457"
email: pinky@acme.com
passwordexpiration: "2023-01-19 23:59:59"
@@ -278,7 +279,6 @@ Example playbook to disable a user:
This can also be done as an alternative with the `users` variable containing only names.
Example playbook to enable users:
```yaml
@@ -297,6 +297,22 @@ Example playbook to enable users:
This can also be done as an alternative with the `users` variable containing only names.
Example playbook to rename users:
```yaml
---
- name: Playbook to handle users
hosts: ipaserver
become: true
tasks:
# Rename user pinky to reddy
- ipauser:
ipaadmin_password: SomeADMINpassword
name: pinky
rename: reddy
state: renamed
```
Example playbook to unlock users:
@@ -352,6 +368,33 @@ Example playbook to ensure users are absent:
state: absent
```
When using FreeIPA 4.8.0+, SMB logon script, profile, home directory and home drive can be set for users.
In the example playbook to set SMB attributes note that `smb_profile_path` and `smb_home_dir` use paths in UNC format, which includes backslashes ('\\`). If the paths are quoted, the backslash needs to be escaped becoming "\\", so the path `\\server\dir` becomes `"\\\\server\\dir"`. If the paths are unquoted the slashes do not have to be escaped.
The YAML specification states that a colon (':') is a key separator and a dash ('-') is an item marker, only with a space after them, so using both unquoted as part of a path should not be a problem. If a space is needed after a colon or a dash, then a quoted string must be used as in `"user - home"`. For the `smb_home_drive` attribute is is recomended that a quoted string is used, to improve readability.
Example playbook to set SMB attributes:
```yaml
---
- name: Plabook to handle users
hosts: ipaserver
become: false
tasks:
- name: Ensure user 'smbuser' is present with smb attributes
ipauser:
ipaadmin_password: SomeADMINpassword
name: smbuser
first: SMB
last: User
smb_logon_script: N:\logonscripts\startup
smb_profile_path: \\server\profiles\some_profile
smb_home_dir: \\users\home\smbuser
smb_home_drive: "U:"
```
Variables
=========
@@ -373,7 +416,7 @@ Variable | Description | Required
`update_password` | Set password for a user in present state only on creation or always. It can be one of `always` or `on_create` and defaults to `always`. | no
`preserve` | Delete a user, keeping the entry available for future use. (bool) | no
`action` | Work on user or member level. It can be on of `member` or `user` and defaults to `user`. | no
`state` | The state to ensure. It can be one of `present`, `absent`, `enabled`, `disabled`, `unlocked` or `undeleted`, default: `present`. Only `names` or `users` with only `name` set are allowed if state is not `present`. | yes
`state` | The state to ensure. It can be one of `present`, `absent`, `enabled`, `disabled`, `renamed`, `unlocked` or `undeleted`, default: `present`. Only `names` or `users` with only `name` set are allowed if state is not `present`. | yes
@@ -393,8 +436,10 @@ 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
`gecos` | GECOS | no
`street` | Street address | no
`city` | City | no
`userstate` \| `st` | State/Province | no
`postalcode` \| `zip` | Postalcode/ZIP | no
@@ -407,7 +452,7 @@ Variable | Description | Required
`manager` | List of manager user names. | no
`carlicense` | List of car licenses. | no
`sshpubkey` \| `ipasshpubkey` | List of SSH public keys. | no
`userauthtype` | List of supported user authentication types. Choices: `password`, `radius`, `otp` and ``. Use empty string to reset userauthtype to the initial value. | no
`userauthtype` \| `ipauserauthtype` | List of supported user authentication types. Choices: `password`, `radius`, `otp`, `pkinit`, `hardened`, `idp` and `""`. An additional check ensures that only types can be used that are supported by the IPA version. Use empty string to reset userauthtype to the initial value. | no
`userclass` | User category. (semantics placed on this attribute are for local interpretation). | no
`radius` | RADIUS proxy configuration | no
`radiususer` | RADIUS proxy username | no
@@ -415,6 +460,8 @@ Variable | Description | Required
`employeenumber` | Employee Number | no
`employeetype` | Employee Type | no
`preferredlanguage` | Preferred Language | no
`idp` \| `ipaidpconfiglink` | External IdP configuration | no
`idp_user_id` \| `ipaidpsub` | A string that identifies the user at external IdP | no
`certificate` | List of base-64 encoded user certificates. | no
`certmapdata` | List of certificate mappings. Either `data` or `certificate` or `issuer` together with `subject` need to be specified. Only usable with IPA versions 4.5 and up. <br>Options: | no
&nbsp; | `certificate` - Base-64 encoded user certificate, not usable with other certmapdata options. | no
@@ -422,10 +469,14 @@ Variable | Description | Required
&nbsp; | `subject` - Subject of the certificate, only usable together with `issuer` option. | no
&nbsp; | `data` - Certmap data, not usable with other certmapdata options. | no
`noprivate` | Do not create user private group. (bool) | no
`smb_logon_script` \| `ipantlogonscript` | SMB logon script path. Requires FreeIPA version 4.8.0+. | no
`smb_profile_path:` \| `ipantprofilepath` | SMB profile path, in UNC format. Requires FreeIPA version 4.8.0+. | no
`smb_home_dir` \| `ipanthomedirectory` | SMB Home Directory, in UNC format. Requires FreeIPA version 4.8.0+. | no
`smb_home_drive` \| `ipanthomedirectorydrive` | SMB Home Directory Drive, a single upercase letter (A-Z) followed by a colon (:), for example "U:". Requires FreeIPA version 4.8.0+. | no
`rename` \| `new_name` | Rename the user object to the new name string. Only usable with `state: renamed`. | no
`nomembers` | Suppress processing of membership attributes. (bool) | no
Return Values
=============
@@ -434,11 +485,12 @@ 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
=======
Thomas Woerner
- Thomas Woerner
- Rafael Jeffman

View File

@@ -24,7 +24,7 @@ Requirements
------------
**Controller**
* Ansible version: 2.8+
* Ansible version: 2.15+
**Node**
* Supported FreeIPA version (see above)

View File

@@ -13,10 +13,12 @@ Features
* Repair mode for clients
* Backup and restore, also to and from controller
* Smartcard setup for servers and clients
* Inventory plugin freeipa
* Modules for automembership rule management
* Modules for automount key management
* Modules for automount location management
* Modules for automount map management
* Modules for certificate management
* Modules for config management
* Modules for delegation management
* Modules for dns config management
@@ -29,7 +31,11 @@ Features
* Modules for hbacsvcgroup management
* Modules for host management
* Modules for hostgroup management
* Modules for idoverridegroup management
* Modules for idoverrideuser management
* Modules for idp management
* Modules for idrange management
* Modules for idview management
* Modules for location management
* Modules for netgroup management
* Modules for permission management
@@ -68,7 +74,7 @@ Requirements
------------
**Controller**
* Ansible version: 2.8+ (ansible-freeipa is an Ansible Collection)
* Ansible version: 2.15+
**Node**
* Supported FreeIPA version (see above)
@@ -103,9 +109,10 @@ You can use the roles directly within the top directory of the git repo, but to
You can either adapt ansible.cfg:
```
roles_path = /my/dir/ansible-freeipa/roles
library = /my/dir/ansible-freeipa/plugins/modules
module_utils = /my/dir/ansible-freeipa/plugins/module_utils
roles_path = /my/dir/ansible-freeipa/roles
library = /my/dir/ansible-freeipa/plugins/modules
module_utils = /my/dir/ansible-freeipa/plugins/module_utils
inventory_plugins = /my/dir/ansible-freeipa/plugins/inventory
```
Or you can link the directories:
@@ -128,18 +135,8 @@ This command will get the whole collection from galaxy:
ansible-galaxy collection install freeipa.ansible_freeipa
```
Installing collections using the ansible-galaxy command is only supported with ansible 2.9+.
The mazer tool can be used for to install the collection for ansible 2.8:
```bash
mazer install freeipa.ansible_freeipa
```
Ansible galaxy does not support the use of dash ('-') in a name and is automatically replacing this with an underscore ('\_'). Therefore the name is `ansible_freeipa`. The ansible_freeipa collection will be placed in the directory `~/.ansible/collections/ansible_collections/freeipa/ansible_freeipa` where it will be automatically be found for this user.
The needed adaptions of collection prefixes for `modules` and `module_utils` will be done with ansible-freeipa release `0.1.6` for galaxy.
Ansible inventory file
----------------------
@@ -436,6 +433,7 @@ Modules in plugin/modules
* [ipaautomountkey](README-automountkey.md)
* [ipaautomountlocation](README-automountlocation.md)
* [ipaautomountmap](README-automountmap.md)
* [ipacert](README-cert.md)
* [ipaconfig](README-config.md)
* [ipadelegation](README-delegation.md)
* [ipadnsconfig](README-dnsconfig.md)
@@ -448,7 +446,11 @@ Modules in plugin/modules
* [ipahbacsvcgroup](README-hbacsvcgroup.md)
* [ipahost](README-host.md)
* [ipahostgroup](README-hostgroup.md)
* [idoverridegroup](README-idoverridegroup.md)
* [idoverrideuser](README-idoverrideuser.md)
* [idp](README-idp.md)
* [idrange](README-idrange.md)
* [idview](README-idview.md)
* [ipalocation](README-location.md)
* [ipanetgroup](README-netgroup.md)
* [ipapermission](README-permission.md)
@@ -470,3 +472,8 @@ Modules in plugin/modules
* [ipavault](README-vault.md)
If you want to write a new module please read [writing a new module](plugins/modules/README.md).
Inventory plugins in plugin/inventory
=====================================
* [freeipa](README-inventory-plugin-freeipa.md)

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

@@ -1,2 +1,2 @@
---
requires_ansible: ">=2.9"
requires_ansible: ">=2.15.0"

View File

@@ -0,0 +1,14 @@
---
- name: Managed automount maps
hosts: ipaserver
become: false
gather_facts: false
tasks:
- name: Playbook to add an indirect automount map
ipaautomountmap:
ipaadmin_password: SomeADMINpassword
name: auto.indirect
location: DMZ
parent: auto.DMZ
mount: dmz_indirect

View File

@@ -0,0 +1,15 @@
---
- name: Certificate manage example
hosts: ipaserver
become: false
gather_facts: false
module_defaults:
ipacert:
ipaadmin_password: SomeADMINpassword
ipaapi_context: client
tasks:
- name: Temporarily hold a certificate
ipacert:
serial_number: 12345
state: held

View File

@@ -0,0 +1,15 @@
---
- name: Certificate manage example
hosts: ipaserver
become: false
gather_facts: false
module_defaults:
ipacert:
ipaadmin_password: SomeADMINpassword
ipaapi_context: client
tasks:
- name: Release a certificate hold
ipacert:
serial_number: 12345
state: released

View File

@@ -0,0 +1,26 @@
---
- name: Certificate manage example
hosts: ipaserver
become: false
gather_facts: false
module_defaults:
ipacert:
ipaadmin_password: SomeADMINpassword
ipaapi_context: client
tasks:
- name: Request a certificate for a host
ipacert:
csr: |
-----BEGIN CERTIFICATE REQUEST-----
MIIBWjCBxAIBADAbMRkwFwYDVQQDDBBob3N0LmV4YW1wbGUuY29tMIGfMA0GCSqG
SIb3DQEBAQUAA4GNADCBiQKBgQCzR3Vd4Cwl0uVgwB3+wxz+4JldFk3x526bPeuK
g8EEc+rEHILzJWeXC8ywCYPOgK9n7hrdMfVQiIx3yHYrY+0IYuLehWow4o1iJEf5
urPNAP9K9C4Y7MMXzzoQmoWR3IFQQpOYwvWOtiZfvrhmtflnYEGLE2tgz53gOQHD
NnbCCwIDAQABoAAwDQYJKoZIhvcNAQELBQADgYEAgF+6YC39WhnvmFgNz7pjAh5E
2ea3CgG+zrzAyiSBGG6WpXEjqMRnAQxciQNGxQacxjwWrscZidZzqg8URJPugewq
tslYB1+RkZn+9UWtfnWvz89+xnOgco7JlytnbH10Nfxt5fXXx13rY0tl54jBtk2W
422eYZ12wb4gjNcQy3A=
-----END CERTIFICATE REQUEST-----
principal: host/host.example.com
state: requested

View File

@@ -0,0 +1,23 @@
---
- name: Certificate manage example
hosts: ipaserver
become: false
gather_facts: false
module_defaults:
ipacert:
ipaadmin_password: SomeADMINpassword
ipaapi_context: client
tasks:
- name: Request a certificate for a service
ipacert:
csr: |
-----BEGIN CERTIFICATE REQUEST-----
MIGYMEwCAQAwGTEXMBUGA1UEAwwOZnJlZWlwYSBydWxlcyEwKjAFBgMrZXADIQBs
HlqIr4b/XNK+K8QLJKIzfvuNK0buBhLz3LAzY7QDEqAAMAUGAytlcANBAF4oSCbA
5aIPukCidnZJdr491G4LBE+URecYXsPknwYb+V+ONnf5ycZHyaFv+jkUBFGFeDgU
SYaXm/gF8cDYjQI=
-----END CERTIFICATE REQUEST-----
principal: HTTP/www.example.com
add: true
state: requested

View File

@@ -0,0 +1,27 @@
---
- name: Certificate manage example
hosts: ipaserver
become: false
gather_facts: false
module_defaults:
ipacert:
ipaadmin_password: SomeADMINpassword
ipaapi_context: client
tasks:
- name: Request a certificate for a user with a specific profile
ipacert:
csr: |
-----BEGIN CERTIFICATE REQUEST-----
MIIBejCB5AIBADAQMQ4wDAYDVQQDDAVwaW5reTCBnzANBgkqhkiG9w0BAQEFAAOB
jQAwgYkCgYEA7uChccy1Is1FTM0SF23WPYW472E3ozeLh2kzhKR9Ni6FLmeEGgu7
/hicR1VwvXHYkNwI1tpW9LqxRVvgr6vheqHySljrBcoRfshfYvKejp03l2327Bfq
BNxXqLcHylNEyg8SH0u63bWyxtgoDBfdZwdGAhYuJ+g4ev79J5eYoB0CAwEAAaAr
MCkGCSqGSIb3DQEJDjEcMBowGAYHKoZIzlYIAQQNDAtoZWxsbyB3b3JsZDANBgkq
hkiG9w0BAQsFAAOBgQADCi5BHDv1mrBFDWqYytFpQ1mrvr/mdax3AYXxNL2UEV8j
AqZAFTEnJXL/u1eVQtI1yotqxakyUBN4XZBP2CBgJRO93Mtry8cgvU1sPdU8Mavx
5gSnlP74Hio2ziscWWydlxpYxFx0gkKvu+0nyIpz954SVYwQ2wwk5FRqZnxI5w==
-----END CERTIFICATE REQUEST-----
principal: pinky
profile: IECUserRoles
state: requested

View File

@@ -0,0 +1,16 @@
---
- name: Certificate manage example
hosts: ipaserver
become: false
gather_facts: false
module_defaults:
ipacert:
ipaadmin_password: SomeADMINpassword
ipaapi_context: client
tasks:
- name: Retrieve a certificate
ipacert:
serial_number: 12345
state: retrieved
register: cert_retrieved

View File

@@ -0,0 +1,18 @@
---
- name: Certificate manage example
hosts: ipaserver
become: false
gather_facts: false
module_defaults:
ipacert:
ipaadmin_password: SomeADMINpassword
ipaapi_context: client
tasks:
- name: Permanently revoke a certificate issued by a lightweight sub-CA
ipacert:
serial_number: 12345
ca: vpn-ca
# reason: keyCompromise (1)
reason: 1
state: revoked

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

@@ -0,0 +1,13 @@
---
- name: Playbook to manage idoverridegroup
hosts: ipaserver
become: no
tasks:
- name: Ensure idoverridegroup test_group is absent in idview test_idview.
ipaidoverridegroup:
ipaadmin_password: SomeADMINpassword
idview: test_idview
anchor: test_group
continue: true
state: absent

View File

@@ -0,0 +1,11 @@
---
- name: Playbook to manage idoverridegroup
hosts: ipaserver
become: no
tasks:
- name: Ensure idoverridegroup test_group is present in idview test_idview.
ipaidoverridegroup:
ipaadmin_password: SomeADMINpassword
idview: test_idview
anchor: test_group

View File

@@ -0,0 +1,13 @@
---
- name: Playbook to manage idoverrideuser
hosts: ipaserver
become: false
tasks:
- name: Ensure test user test_user is absent in idview test_idview
ipaidoverrideuser:
ipaadmin_password: SomeADMINpassword
idview: test_idview
anchor: test_user
continue: true
state: absent

View File

@@ -0,0 +1,15 @@
---
- name: Playbook to manage idoverrideuser
hosts: ipaserver
become: false
tasks:
- name: Ensure test user test_user certificate member is absent in idview test_idview
ipaidoverrideuser:
ipaadmin_password: SomeADMINpassword
idview: test_idview
anchor: test_user
certificate:
- "{{ lookup('file', 'cert1.b64', rstrip=False) }}"
action: member
state: absent

View File

@@ -0,0 +1,14 @@
---
- name: Playbook to manage idoverrideuser
hosts: ipaserver
become: false
tasks:
- name: Ensure test user test_user certificate member is present in idview test_idview
ipaidoverrideuser:
ipaadmin_password: SomeADMINpassword
idview: test_idview
anchor: test_user
certificate:
- "{{ lookup('file', 'cert1.b64', rstrip=False) }}"
action: member

View File

@@ -0,0 +1,11 @@
---
- name: Playbook to manage idoverrideuser
hosts: ipaserver
become: false
tasks:
- name: Ensure test user test_user is present in idview test_idview.
ipaidoverrideuser:
ipaadmin_password: SomeADMINpassword
idview: test_idview
anchor: test_user

View File

@@ -0,0 +1,11 @@
---
- name: Idp absent example
hosts: ipaserver
become: no
tasks:
- name: Ensure github idp my-github-idp is absent
ipaidp:
ipaadmin_password: SomeADMINpassword
name: my-github-idp
state: absent

View File

@@ -0,0 +1,12 @@
---
- name: Idp present example
hosts: ipaserver
become: no
tasks:
- name: Ensure github idp my-github-idp is present
ipaidp:
ipaadmin_password: SomeADMINpassword
name: my-github-idp
provider: github
client_id: my-github-client-id

View File

@@ -0,0 +1,11 @@
---
- name: Idview absent example
hosts: ipaserver
become: no
tasks:
- name: Ensure idview test_idview is absent
ipaidview:
ipaadmin_password: SomeADMINpassword
name: test_idview
state: absent

View File

@@ -0,0 +1,12 @@
---
- name: Idview host member applied example
hosts: ipaserver
become: no
tasks:
- name: Ensure host testhost.example.com is applied to idview test_idview
ipaidview:
ipaadmin_password: SomeADMINpassword
name: test_idview
host: testhost.example.com
action: member

View File

@@ -0,0 +1,13 @@
---
- name: Idview host member unapplied example
hosts: ipaserver
become: no
tasks:
- name: Ensure host testhost.example.com is not applied to idview test_idview
ipaidview:
ipaadmin_password: SomeADMINpassword
name: test_idview
host: testhost.example.com
action: member
state: absent

View File

@@ -0,0 +1,10 @@
---
- name: Idview present example
hosts: ipaserver
become: no
tasks:
- name: Ensure idview test_idview is present
ipaidview:
ipaadmin_password: SomeADMINpassword
name: test_idview

View File

@@ -0,0 +1,14 @@
---
- name: Playbook to manage sudorule member
hosts: ipaserver
become: no
gather_facts: no
tasks:
- name: Ensure sudorule 'runasuser' do not have 'ipasuers' group as runas users.
ipasudorule:
ipaadmin_password: SomeADMINpassword
name: testrule1
runasuser_group: ipausers
action: member
state: absent

View File

@@ -0,0 +1,13 @@
---
- name: Playbook to manage sudorule member
hosts: ipaserver
become: no
gather_facts: no
tasks:
- name: Ensure sudorule 'runasuser' has 'ipasuers' group as runas users.
ipasudorule:
ipaadmin_password: SomeADMINpassword
name: testrule1
runasuser_group: ipausers
action: member

View File

@@ -0,0 +1,12 @@
---
- name: Playbook to handle users
hosts: ipaserver
become: true
tasks:
- name: Create user associated with an external IdP
ipauser:
ipaadmin_password: SomeADMINpassword
name: idpuser
idp: keycloak
idp_user_id: idpuser@exemple.com

View File

@@ -0,0 +1,17 @@
---
- name: Plabook to handle users
hosts: ipaserver
become: false
gather_facts: false
tasks:
- name: Ensure user 'smbuser' is present with smb attributes
ipauser:
ipaadmin_password: SomeADMINpassword
name: smbuser
first: SMB
last: User
smb_logon_script: N:\logonscripts\startup
smb_profile_path: \\server\profiles\some_profile
smb_home_dir: \\users\home\smbuser
smb_home_drive: "U:"

View File

@@ -56,5 +56,5 @@ options:
Continuous mode. Don't stop on errors. Valid only if `state` is `absent`.
aliases: ["continue"]
type: bool
default: True
default: true
"""

View File

@@ -0,0 +1,191 @@
# -*- coding: utf-8 -*-
# Authors:
# Thomas Woerner <twoerner@redhat.com>
#
# Copyright (C) 2024 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 = """
---
name: freeipa
version_added: "1.13.0"
short_description: Compiles a dynamic inventory from IPA domain
description: |
Compiles a dynamic inventory from IPA domain, filters servers by role(s).
options:
plugin:
description: Marks this as an instance of the "freeipa" plugin.
required: True
choices: ["freeipa"]
ipaadmin_principal:
description: The admin principal.
default: admin
type: str
ipaadmin_password:
description: The admin password.
required: true
type: str
server:
description: FQDN of server to start the scan.
type: str
required: true
verify:
description: |
The server TLS certificate file for verification (/etc/ipa/ca.crt).
Turned off if not set.
type: str
required: false
role:
description: |
The role(s) of the server. If several roles are given, only servers
that have all the roles are returned.
type: list
elements: str
choices: ["IPA master", "CA server", "KRA server", "DNS server",
"AD trust controller", "AD trust agent"]
required: false
inventory_group:
description: |
The inventory group to create. The default group name is "ipaservers".
type: str
default: ipaservers
author:
- Thomas Woerner (@t-woerner)
"""
EXAMPLES = """
# inventory.config file in YAML format
plugin: freeipa
server: ipaserver-01.ipa.local
ipaadmin_password: SomeADMINpassword
# inventory.config file in YAML format with server TLS certificate verification
plugin: freeipa
server: ipaserver-01.ipa.local
ipaadmin_password: SomeADMINpassword
verify: ca.crt
"""
import os
try:
import requests
except ImportError:
requests = None
try:
import urllib3
except ImportError:
urllib3 = None
from ansible import constants
from ansible.errors import AnsibleParserError
from ansible.module_utils.common.text.converters import to_native
from ansible.plugins.inventory import BaseInventoryPlugin
from ansible.module_utils.six.moves.urllib.parse import quote
class InventoryModule(BaseInventoryPlugin):
NAME = 'freeipa'
def verify_file(self, path):
# pylint: disable=super-with-arguments
if super(InventoryModule, self).verify_file(path):
_name, ext = os.path.splitext(path)
if ext in constants.YAML_FILENAME_EXTENSIONS:
return True
return False
def parse(self, inventory, loader, path, cache=False):
# pylint: disable=super-with-arguments
super(InventoryModule, self).parse(inventory, loader, path,
cache=cache)
self._read_config_data(path) # This also loads the cache
self.get_option("plugin")
if requests is None:
raise AnsibleParserError("The required Python library "
"'requests' could not be imported.")
ipaadmin_principal = self.get_option("ipaadmin_principal")
ipaadmin_password = self.get_option("ipaadmin_password")
server = self.get_option("server")
verify = self.get_option("verify")
role = self.get_option("role")
inventory_group = self.get_option("inventory_group")
if verify is not None:
if not os.path.exists(verify):
raise AnsibleParserError("ERROR: Could not load %s" % verify)
else:
verify = False
# Disable certificate verification warning without certificate
# as long as urllib3 could have been loaded.
if urllib3 is not None:
urllib3.disable_warnings(
urllib3.exceptions.InsecureRequestWarning)
self.inventory.add_group(inventory_group)
ipa_url = "https://%s/ipa" % server
s = requests.Session()
s.headers.update({"referer": ipa_url})
s.headers.update({"Content-Type":
"application/x-www-form-urlencoded"})
s.headers.update({"Accept": "text/plain"})
data = 'user=%s&password=%s' % (quote(ipaadmin_principal, safe=''),
quote(ipaadmin_password, safe=''))
response = s.post("%s/session/login_password" % ipa_url,
data=data, verify=verify)
# Now use json API
s.headers.update({"Content-Type": "application/json"})
kw_args = {}
if role is not None:
kw_args["servrole"] = role
json_data = {
"method" : "server_find",
"params": [[], kw_args],
"id": 0
}
response = s.post("%s/session/json" % ipa_url, json=json_data,
verify=verify)
json_res = response.json()
error = json_res.get("error")
if error is not None:
raise AnsibleParserError("ERROR: %s" % to_native(error))
if "result" in json_res and "result" in json_res["result"]:
res = json_res["result"].get("result")
if isinstance(res, list):
for server in res:
self.inventory.add_host(server["cn"][0],
group=inventory_group)

View File

@@ -25,11 +25,24 @@ from __future__ import (absolute_import, division, print_function)
__metaclass__ = type
__all__ = ["gssapi", "netaddr", "api", "ipalib_errors", "Env",
__all__ = ["DEBUG_COMMAND_ALL", "DEBUG_COMMAND_LIST",
"DEBUG_COMMAND_COUNT", "DEBUG_COMMAND_BATCH",
"gssapi", "netaddr", "api", "ipalib_errors", "Env",
"DEFAULT_CONFIG", "LDAP_GENERALIZED_TIME_FORMAT",
"kinit_password", "kinit_keytab", "run", "DN", "VERSION",
"paths", "tasks", "get_credentials_if_valid", "Encoding",
"load_pem_x509_certificate", "DNSName", "getargspec"]
"DNSName", "getargspec", "certificate_loader",
"write_certificate_list", "boolean", "template_str",
"urlparse", "normalize_sshpubkey"]
DEBUG_COMMAND_ALL = 0b1111
# Print the while command list:
DEBUG_COMMAND_LIST = 0b0001
# Print the number of commands:
DEBUG_COMMAND_COUNT = 0b0010
# Print information about the batch slice size and currently executed batch
# slice:
DEBUG_COMMAND_BATCH = 0b0100
import os
# ansible-freeipa requires locale to be C, IPA requires utf-8.
@@ -41,6 +54,9 @@ import tempfile
import shutil
import socket
import base64
import binascii
import ast
import time
from datetime import datetime
from contextlib import contextmanager
from ansible.module_utils.basic import AnsibleModule
@@ -48,6 +64,7 @@ from ansible.module_utils._text import to_text
from ansible.module_utils.common.text.converters import jsonify
from ansible.module_utils import six
from ansible.module_utils.common._collections_compat import Mapping
from ansible.module_utils.parsing.convert_bool import boolean
# Import getargspec from inspect or provide own getargspec for
# Python 2 compatibility with Python 3.11+.
@@ -83,10 +100,15 @@ try:
from ipalib.constants import DEFAULT_CONFIG, LDAP_GENERALIZED_TIME_FORMAT
try:
from ipalib.install.kinit import kinit_password, kinit_keytab
from ipalib.kinit import kinit_password, kinit_keytab
except ImportError:
from ipapython.ipautil import kinit_password, kinit_keytab
try:
from ipalib.install.kinit import kinit_password, kinit_keytab
except ImportError:
# pre 4.5.0
from ipapython.ipautil import kinit_password, kinit_keytab
from ipapython.ipautil import run
from ipapython.ipautil import template_str
from ipapython.dn import DN
from ipapython.version import VERSION
from ipaplatform.paths import paths
@@ -106,6 +128,7 @@ try:
except ImportError:
from ipalib.x509 import load_certificate
certificate_loader = load_certificate
from ipalib.x509 import write_certificate_list
# Try to import is_ipa_configured or use a fallback implementation.
try:
@@ -142,6 +165,13 @@ try:
except ImportError:
_dcerpc_bindings_installed = False # pylint: disable=invalid-name
try:
from urllib.parse import urlparse
except ImportError:
from ansible.module_utils.six.moves.urllib.parse import urlparse
from ipalib.util import normalize_sshpubkey
except ImportError as _err:
ANSIBLE_FREEIPA_MODULE_IMPORT_ERROR = str(_err)
@@ -222,7 +252,7 @@ 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']
os.environ.pop('KRB5CCNAME', None)
if ccache_dir is not None:
shutil.rmtree(ccache_dir, ignore_errors=True)
@@ -459,20 +489,22 @@ def _afm_convert(value):
return value
def module_params_get(module, name, allow_empty_string=False):
def module_params_get(module, name, allow_empty_list_item=False):
value = _afm_convert(module.params.get(name))
# Fail on empty strings in the list or if allow_empty_string is True
# if there is another entry in the list together with the empty
# string.
# Fail on empty strings in the list or if allow_empty_list_item is True
# if there is another entry in the list together with the empty string.
# Due to an issue in Ansible it is possible to use the empty string
# "" for lists with choices, even if the empty list is not part of
# the choices.
# Ansible issue https://github.com/ansible/ansible/issues/77108
if isinstance(value, list):
for val in value:
if isinstance(val, (str, unicode)) and not val:
if not allow_empty_string:
if (
isinstance(val, (str, unicode)) # pylint: disable=W0012,E0606
and not val
):
if not allow_empty_list_item:
module.fail_json(
msg="Parameter '%s' contains an empty string" %
name)
@@ -484,8 +516,8 @@ def module_params_get(module, name, allow_empty_string=False):
return value
def module_params_get_lowercase(module, name, allow_empty_string=False):
value = module_params_get(module, name, allow_empty_string)
def module_params_get_lowercase(module, name, allow_empty_list_item=False):
value = module_params_get(module, name, allow_empty_list_item)
if isinstance(value, list):
value = [v.lower() for v in value]
if isinstance(value, (str, unicode)):
@@ -493,6 +525,48 @@ def module_params_get_lowercase(module, name, allow_empty_string=False):
return value
def module_params_get_with_type_cast(
module, name, datatype, allow_empty=False
):
"""
Retrieve value set for module parameter as a specific data type.
Parameters
----------
module: AnsibleModule
The module from where to get the parameter value from.
name: string
The name of the parameter to retrieve.
datatype: type
The type to convert the value to, if value is not empty.
allow_empty: bool
Allow an empty string for non list parameters or a list
containing (only) an empty string item. This is used for
resetting parameters to the default value.
"""
value = module_params_get(module, name, allow_empty)
if not allow_empty and value == "":
module.fail_json(
msg="Argument '%s' must not be an empty string" % (name,)
)
if value is not None and value != "":
try:
if datatype is bool:
# We let Ansible handle bool values
value = boolean(value)
else:
value = datatype(value)
except ValueError:
module.fail_json(
msg="Invalid value '%s' for argument '%s'" % (value, name)
)
except TypeError as terr:
# If Ansible fails to parse a boolean, it will raise TypeError
module.fail_json(msg="Param '%s': %s" % (name, str(terr)))
return value
def api_get_domain():
return api.env.domain
@@ -571,6 +645,7 @@ def encode_certificate(cert):
Encode a certificate using base64.
It also takes FreeIPA and Python versions into account.
This is used to convert the certificates returned by find and show.
"""
if isinstance(cert, (str, unicode, bytes)):
encoded = base64.b64encode(cert)
@@ -581,6 +656,33 @@ def encode_certificate(cert):
return encoded
def convert_input_certificates(module, certs, state):
"""
Convert certificates.
Remove all newlines and white spaces from the certificates.
This is used on input parameter certificates of modules.
"""
if certs is None:
return None
_certs = []
for cert in certs:
try:
_cert = base64.b64encode(base64.b64decode(cert)).decode("ascii")
except (TypeError, binascii.Error) as e:
# Idempotency: Do not fail for an invalid cert for state absent.
# The invalid certificate can not be set in FreeIPA.
if state == "absent":
continue
module.fail_json(
msg="Certificate %s: Base64 decoding failed: %s" %
(repr(cert), str(e)))
_certs.append(_cert)
return _certs
def load_cert_from_str(cert):
cert = cert.strip()
if not cert.startswith("-----BEGIN CERTIFICATE-----"):
@@ -747,8 +849,8 @@ def exit_raw_json(module, **kwargs):
contains sensible data, it will be appear in the logs.
"""
module.do_cleanup_files()
print(jsonify(kwargs))
sys.exit(0)
print(jsonify(kwargs)) # pylint: disable=W0012,ansible-bad-function
sys.exit(0) # pylint: disable=W0012,ansible-bad-function
def __get_domain_validator():
@@ -1040,7 +1142,7 @@ class IPAAnsibleModule(AnsibleModule):
finally:
temp_kdestroy(ccache_dir, ccache_name)
def params_get(self, name, allow_empty_string=False):
def params_get(self, name, allow_empty_list_item=False):
"""
Retrieve value set for module parameter.
@@ -1048,13 +1150,13 @@ class IPAAnsibleModule(AnsibleModule):
----------
name: string
The name of the parameter to retrieve.
allow_empty_string: bool
allow_empty_list_item: bool
The parameter allowes to have empty strings in a list
"""
return module_params_get(self, name, allow_empty_string)
return module_params_get(self, name, allow_empty_list_item)
def params_get_lowercase(self, name, allow_empty_string=False):
def params_get_lowercase(self, name, allow_empty_list_item=False):
"""
Retrieve value set for module parameter as lowercase, if not None.
@@ -1062,11 +1164,34 @@ class IPAAnsibleModule(AnsibleModule):
----------
name: string
The name of the parameter to retrieve.
allow_empty_string: bool
allow_empty_list_item: bool
The parameter allowes to have empty strings in a list
"""
return module_params_get_lowercase(self, name, allow_empty_string)
return module_params_get_lowercase(self, name, allow_empty_list_item)
def params_get_with_type_cast(
self, name, datatype, allow_empty=True
):
"""
Retrieve value set for module parameter as a specific data type.
Parameters
----------
name: string
The name of the parameter to retrieve.
datatype: type
The type to convert the value to, if not empty.
datatype: type
The type to convert the value to, if value is not empty.
allow_empty: bool
Allow an empty string for non list parameters or a list
containing (only) an empty string item. This is used for
resetting parameters to the default value.
"""
return module_params_get_with_type_cast(
self, name, datatype, allow_empty)
def params_fail_used_invalid(self, invalid_params, state, action=None):
"""
@@ -1166,6 +1291,45 @@ class IPAAnsibleModule(AnsibleModule):
"""
return api_check_param(command, name)
def ipa_command_invalid_param_choices(self, command, name, value):
"""
Return invalid parameter choices for IPA command.
Parameters
----------
command: string
The IPA API command to test.
name: string
The parameter name to check.
value: string
The parameter value to verify.
"""
if command not in api.Command:
self.fail_json(msg="The command '%s' does not exist." % command)
if name not in api.Command[command].params:
self.fail_json(msg="The command '%s' does not have a parameter "
"named '%s'." % (command, name))
if not hasattr(api.Command[command].params[name], "cli_metavar"):
self.fail_json(msg="The parameter '%s' of the command '%s' does "
"not have choices." % (name, command))
# For IPA 4.6 (RHEL-7):
# - krbprincipalauthind in host_add does not have choices defined
# - krbprincipalauthind in service_add does not have choices defined
#
# api.Command[command].params[name].cli_metavar returns "STR" and
# ast.literal_eval failes with a ValueError "malformed string".
#
# There is no way to verify that the given values are valid or not in
# this case. The check is done later on while applying the change
# with host_add, host_mod, service_add and service_mod.
try:
_choices = ast.literal_eval(
api.Command[command].params[name].cli_metavar)
except ValueError:
return None
return (set(value or []) - set([""])) - set(_choices)
@staticmethod
def ipa_check_version(oper, requested_version):
"""
@@ -1195,7 +1359,8 @@ class IPAAnsibleModule(AnsibleModule):
def execute_ipa_commands(self, commands, result_handler=None,
exception_handler=None,
fail_on_member_errors=False,
**handlers_user_args):
batch=False, batch_slice_size=100, debug=False,
keeponly=None, **handlers_user_args):
"""
Execute IPA API commands from command list.
@@ -1212,6 +1377,16 @@ class IPAAnsibleModule(AnsibleModule):
Returns True to continue to next command, else False
fail_on_member_errors: bool
Use default member error handler handler member_error_handler
batch: bool
Enable batch command use to speed up processing
batch_slice_size: integer
Maximum mumber of commands processed in a slice with the batch
command
keeponly: list of string
The attributes to keep in the results returned from the commands
Default: None (Keep all)
debug: integer
Enable debug output for the exection using DEBUG_COMMAND_*
handlers_user_args: dict (user args mapping)
The user args to pass to result_handler and exception_handler
functions
@@ -1281,34 +1456,123 @@ class IPAAnsibleModule(AnsibleModule):
if "errors" in argspec.args:
handlers_user_args["errors"] = _errors
if debug & DEBUG_COMMAND_LIST:
self.tm_warn("commands: %s" % repr(commands))
if debug & DEBUG_COMMAND_COUNT:
self.tm_warn("#commands: %s" % len(commands))
# Turn off batch use for server context when it lacks the keeponly
# option as it lacks https://github.com/freeipa/freeipa/pull/7335
# This is an important fix about reporting errors in the batch
# (example: "no modifications to be performed") that results in
# aborted processing of the batch and an error about missing
# attribute principal. FreeIPA issue #9583
batch_has_keeponly = "keeponly" in api.Command.batch.options
if batch and api.env.in_server and not batch_has_keeponly:
self.debug(
"Turning off batch processing for batch missing keeponly")
batch = False
changed = False
for name, command, args in commands:
try:
if name is None:
result = self.ipa_command_no_name(command, args)
else:
result = self.ipa_command(command, name, args)
if batch:
# batch processing
batch_args = []
for ci, (name, command, args) in enumerate(commands):
if len(batch_args) < batch_slice_size:
batch_args.append({
"method": command,
"params": ([name], args)
})
if "completed" in result:
if result["completed"] > 0:
changed = True
else:
changed = True
# If result_handler is not None, call it with user args
# defined in **handlers_user_args
if result_handler is not None:
result_handler(self, result, command, name, args,
**handlers_user_args)
except Exception as e:
if exception_handler is not None and \
exception_handler(self, e, **handlers_user_args):
if len(batch_args) < batch_slice_size and \
ci < len(commands) - 1:
# fill in more commands untill batch slice size is reached
# or final slice of commands
continue
self.fail_json(msg="%s: %s: %s" % (command, name, str(e)))
if debug & DEBUG_COMMAND_BATCH:
self.tm_warn("batch %d (size %d/%d)" %
(ci / batch_slice_size, len(batch_args),
batch_slice_size))
# run the batch command
if batch_has_keeponly:
result = api.Command.batch(batch_args, keeponly=keeponly)
else:
result = api.Command.batch(batch_args)
if len(batch_args) != result["count"]:
self.fail_json(
"Result size %d does not match batch size %d" % (
result["count"], len(batch_args)))
if result["count"] > 0:
for ri, res in enumerate(result["results"]):
_res = res.get("result", None)
if not batch_has_keeponly and keeponly is not None \
and isinstance(_res, dict):
res["result"] = dict(
filter(lambda x: x[0] in keeponly,
_res.items())
)
if "error" not in res or res["error"] is None:
if result_handler is not None:
result_handler(
self, res,
batch_args[ri]["method"],
batch_args[ri]["params"][0][0],
batch_args[ri]["params"][1],
**handlers_user_args)
changed = True
else:
_errors.append(
"%s: %s: %s" %
(batch_args[ri]["method"],
str(batch_args[ri]["params"][0][0]),
res["error"]))
# clear batch command list (python2 compatible)
del batch_args[:]
else:
# no batch processing
for name, command, args in commands:
try:
if name is None:
result = self.ipa_command_no_name(command, args)
else:
result = self.ipa_command(command, name, args)
if "completed" in result:
if result["completed"] > 0:
changed = True
else:
changed = True
# Handle keeponly
res = result.get("result", None)
if keeponly is not None and isinstance(res, dict):
result["result"] = dict(
filter(lambda x: x[0] in keeponly, res.items())
)
# If result_handler is not None, call it with user args
# defined in **handlers_user_args
if result_handler is not None:
result_handler(self, result, command, name, args,
**handlers_user_args)
except Exception as e:
if exception_handler is not None and \
exception_handler(self, e, **handlers_user_args):
continue
self.fail_json(msg="%s: %s: %s" % (command, name, str(e)))
# Fail on errors from result_handler and exception_handler
if len(_errors) > 0:
self.fail_json(msg=", ".join(_errors))
return changed
def tm_warn(self, warning):
ts = time.time()
# pylint: disable=super-with-arguments
super(IPAAnsibleModule, self).warn("%f %s" % (ts, warning))

View File

@@ -450,6 +450,10 @@ def main():
commands = []
for name in names:
_type = None
inclusive_add, inclusive_del = [], []
exclusive_add, exclusive_del = [], []
# Make sure automember rule exists
res_find = find_automember(ansible_module, name, automember_type)
@@ -495,16 +499,12 @@ def main():
transform_conditions(inclusive),
res_find.get("automemberinclusiveregex", [])
)
else:
inclusive_add, inclusive_del = [], []
if exclusive is not None:
exclusive_add, exclusive_del = gen_add_del_lists(
transform_conditions(exclusive),
res_find.get("automemberexclusiveregex", [])
)
else:
exclusive_add, exclusive_del = [], []
elif action == "member":
if res_find is None:
@@ -512,9 +512,7 @@ def main():
msg="No automember '%s'" % name)
inclusive_add = transform_conditions(inclusive or [])
inclusive_del = []
exclusive_add = transform_conditions(exclusive or [])
exclusive_del = []
for _inclusive in inclusive_add:
key, regex = _inclusive.split("=", 1)

View File

@@ -121,8 +121,7 @@ class AutomountKey(IPAAnsibleModule):
resp = self.ipa_command("automountkey_show", location, args)
except ipalib_errors.NotFound:
return None
else:
return resp.get("result")
return resp.get("result")
def check_ipa_params(self):
invalid = []

View File

@@ -92,8 +92,7 @@ class AutomountLocation(IPAAnsibleModule):
)
except ipalib_errors.NotFound:
return None
else:
return response.get("result", None)
return response.get("result", None)
def check_ipa_params(self):
if len(self.params_get("name")) == 0:

View File

@@ -37,6 +37,7 @@ module: ipaautomountmap
author:
- Chris Procter (@chr15p)
- Thomas Woerner (@t-woerner)
- Rafael Jeffman (@rjeffman)
short_description: Manage FreeIPA autommount map
description:
- Add, delete, and modify an IPA automount map
@@ -59,6 +60,16 @@ options:
type: str
aliases: ["description"]
required: false
parentmap:
description: |
Parent map of the indirect map. Can only be used when creating
new maps.
type: str
required: false
mount:
description: Indirect map mount point, relative to parent map.
type: str
required: false
state:
description: State to ensure
type: str
@@ -75,6 +86,14 @@ EXAMPLES = '''
location: DMZ
desc: "this is a map for servers in the DMZ"
- name: ensure indirect map exists
ipaautomountmap:
ipaadmin_password: SomeADMINpassword
name: auto.INDIRECT
location: DMZ
parentmap: auto.DMZ
mount: indirect
- name: remove a map named auto.DMZ in location DMZ if it exists
ipaautomountmap:
ipaadmin_password: SomeADMINpassword
@@ -107,8 +126,36 @@ class AutomountMap(IPAAnsibleModule):
)
except Exception: # pylint: disable=broad-except
return None
else:
return response["result"]
return response["result"]
def get_indirect_map_keys(self, location, name):
"""Check if 'name' is an indirect map for 'parentmap'."""
try:
maps = self.ipa_command("automountmap_find", location, {})
except Exception: # pylint: disable=broad-except
return []
result = []
for check_map in maps.get("result", []):
_mapname = check_map['automountmapname'][0]
keys = self.ipa_command(
"automountkey_find",
location,
{
"automountmapautomountmapname": _mapname,
"all": True
}
)
cmp_value = (
name if _mapname == "auto.master" else "ldap:{0}".format(name)
)
result.extend([
(location, _mapname, key.get("automountkey")[0])
for key in keys.get("result", [])
for mount_info in key.get("automountinformation", [])
if cmp_value in mount_info
])
return result
def check_ipa_params(self):
invalid = []
@@ -118,15 +165,27 @@ class AutomountMap(IPAAnsibleModule):
if len(name) != 1:
self.fail_json(msg="Exactly one name must be provided for"
" 'state: present'.")
mount = self.params_get("mount") or False
parentmap = self.params_get("parentmap")
if parentmap:
if not mount:
self.fail_json(
msg="Must provide 'mount' parameter for indirect map."
)
elif parentmap != "auto.master" and mount[0] == "/":
self.fail_json(
msg="mount point is relative to parent map, "
"cannot begin with '/'"
)
if state == "absent":
if len(name) == 0:
self.fail_json(msg="At least one 'name' must be provided for"
" 'state: absent'")
invalid = ["desc"]
invalid = ["desc", "parentmap", "mount"]
self.params_fail_used_invalid(invalid, state)
def get_args(self, mapname, desc):
def get_args(self, mapname, desc, parentmap, mount):
# automountmapname is required for all automountmap operations.
if not mapname:
self.fail_json(msg="automountmapname cannot be None or empty.")
@@ -134,6 +193,11 @@ class AutomountMap(IPAAnsibleModule):
# An empty string is valid and will clear the attribute.
if desc is not None:
_args["description"] = desc
# indirect map attributes
if parentmap is not None:
_args["parentmap"] = parentmap
if mount is not None:
_args["key"] = mount
return _args
def define_ipa_commands(self):
@@ -141,28 +205,102 @@ class AutomountMap(IPAAnsibleModule):
state = self.params_get("state")
location = self.params_get("location")
desc = self.params_get("desc")
mount = self.params_get("mount")
parentmap = self.params_get("parentmap")
for mapname in name:
automountmap = self.get_automountmap(location, mapname)
is_indirect_map = any([parentmap, mount])
if state == "present":
args = self.get_args(mapname, desc)
args = self.get_args(mapname, desc, parentmap, mount)
if automountmap is None:
self.commands.append([location, "automountmap_add", args])
if is_indirect_map:
if (
parentmap and
self.get_automountmap(location, parentmap) is None
):
self.fail_json(msg="Parent map does not exist.")
self.commands.append(
[location, "automountmap_add_indirect", args]
)
else:
self.commands.append(
[location, "automountmap_add", args]
)
else:
if not compare_args_ipa(self, args, automountmap):
has_changes = not compare_args_ipa(
self, args, automountmap, ['parentmap', 'key']
)
if is_indirect_map:
map_config = (
location, parentmap or "auto.master", mount
)
indirects = self.get_indirect_map_keys(
location, mapname
)
if map_config not in indirects or has_changes:
self.fail_json(
msg="Indirect maps can only be created, "
"not modified."
)
elif has_changes:
self.commands.append(
[location, "automountmap_mod", args]
)
if state == "absent":
elif state == "absent":
def find_keys(parent_loc, parent_map, parent_key):
return self.ipa_command(
"automountkey_show",
parent_loc,
{
"automountmapautomountmapname": parent_map,
"automountkey": parent_key,
}
).get("result")
if automountmap is not None:
indirects = self.get_indirect_map_keys(location, mapname)
# Remove indirect map configurations for this map
self.commands.extend([
(
ploc,
"automountkey_del",
{
"automountmapautomountmapname": pmap,
"automountkey": pkey,
}
)
for ploc, pmap, pkey in indirects
if find_keys(ploc, pmap, pkey)
])
# Remove map
self.commands.append([
location,
"automountmap_del",
{"automountmapname": [mapname]}
])
# ensure commands are unique and automountkey commands are
# executed first in the list
def hashable_dict(dictionaire):
return tuple(
(k, tuple(v) if isinstance(v, (list, tuple)) else v)
for k, v in dictionaire.items()
)
cmds = [
(name, cmd, hashable_dict(args))
for name, cmd, args in self.commands
]
self.commands = [
(name, cmd, dict(args))
for name, cmd, args in
sorted(set(cmds), key=lambda cmd: cmd[1])
]
def main():
ipa_module = AutomountMap(
@@ -184,6 +322,10 @@ def main():
required=False,
default=None
),
parentmap=dict(
type="str", required=False, default=None
),
mount=dict(type="str", required=False, default=None),
),
)
changed = False

574
plugins/modules/ipacert.py Normal file
View File

@@ -0,0 +1,574 @@
#!/usr/bin/python
# -*- coding: utf-8 -*-
# Authors:
# Sam Morris <sam@robots.org.uk>
# Rafael Guterres Jeffman <rjeffman@redhat.com>
#
# Copyright (C) 2021 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: ipacert
short_description: Manage FreeIPA certificates
description: Manage FreeIPA certificates
extends_documentation_fragment:
- ipamodule_base_docs
options:
csr:
description: |
X509 certificate signing request, in RFC 7468 PEM encoding.
Only available if `state: requested`, required if `csr_file` is not
provided.
type: str
csr_file:
description: |
Path to file with X509 certificate signing request, in RFC 7468 PEM
encoding. Only available if `state: requested`, required if `csr_file`
is not provided.
type: str
principal:
description: |
Host/service/user principal for the certificate.
Required if `state: requested`. Only available if `state: requested`.
type: str
add:
description: |
Automatically add the principal if it doesn't exist (service
principals only). Only available if `state: requested`.
type: bool
aliases: ["add_principal"]
required: false
ca:
description: Name of the issuing certificate authority.
type: str
required: false
chain:
description: Include certificate chain in output.
type: bool
required: false
serial_number:
description: |
Certificate serial number. Cannot be used with `state: requested`.
Required for all states, except `requested`.
type: int
profile:
description: Certificate Profile to use.
type: str
aliases: ["profile_id"]
required: false
revocation_reason:
description: |
Reason for revoking the certificate. Use one of the reason strings,
or the corresponding value: "unspecified" (0), "keyCompromise" (1),
"cACompromise" (2), "affiliationChanged" (3), "superseded" (4),
"cessationOfOperation" (5), "certificateHold" (6), "removeFromCRL" (8),
"privilegeWithdrawn" (9), "aACompromise" (10).
Use only if `state: revoked`. Required if `state: revoked`.
type: raw
aliases: ['reason']
certificate_out:
description: |
Write certificate (chain if `chain` is set) to this file, on the target
node.. Use only when `state` is `requested` or `retrieved`.
type: str
required: false
state:
description: |
The state to ensure. `held` is the same as revoke with reason
"certificateHold" (6). `released` is the same as `cert-revoke-hold`
on IPA CLI, releasing the hold status of a certificate.
choices: ["requested", "held", "released", "revoked", "retrieved"]
required: true
type: str
author:
- Sam Morris (@yrro)
- Rafael Guterres Jeffman (@rjeffman)
"""
EXAMPLES = """
- name: Request a certificate for a web server
ipacert:
ipaadmin_password: SomeADMINpassword
state: requested
csr: |
-----BEGIN CERTIFICATE REQUEST-----
MIGYMEwCAQAwGTEXMBUGA1UEAwwOZnJlZWlwYSBydWxlcyEwKjAFBgMrZXADIQBs
HlqIr4b/XNK+K8QLJKIzfvuNK0buBhLz3LAzY7QDEqAAMAUGAytlcANBAF4oSCbA
5aIPukCidnZJdr491G4LBE+URecYXsPknwYb+V+ONnf5ycZHyaFv+jkUBFGFeDgU
SYaXm/gF8cDYjQI=
-----END CERTIFICATE REQUEST-----
principal: HTTP/www.example.com
register: cert
- name: Request certificate for a user, with an appropriate profile.
ipacert:
ipaadmin_password: SomeADMINpassword
csr: |
-----BEGIN CERTIFICATE REQUEST-----
MIIBejCB5AIBADAQMQ4wDAYDVQQDDAVwaW5reTCBnzANBgkqhkiG9w0BAQEFAAOB
jQAwgYkCgYEA7uChccy1Is1FTM0SF23WPYW472E3ozeLh2kzhKR9Ni6FLmeEGgu7
/hicR1VwvXHYkNwI1tpW9LqxRVvgr6vheqHySljrBcoRfshfYvKejp03l2327Bfq
BNxXqLcHylNEyg8SH0u63bWyxtgoDBfdZwdGAhYuJ+g4ev79J5eYoB0CAwEAAaAr
MCkGCSqGSIb3DQEJDjEcMBowGAYHKoZIzlYIAQQNDAtoZWxsbyB3b3JsZDANBgkq
hkiG9w0BAQsFAAOBgQADCi5BHDv1mrBFDWqYytFpQ1mrvr/mdax3AYXxNL2UEV8j
AqZAFTEnJXL/u1eVQtI1yotqxakyUBN4XZBP2CBgJRO93Mtry8cgvU1sPdU8Mavx
5gSnlP74Hio2ziscWWydlxpYxFx0gkKvu+0nyIpz954SVYwQ2wwk5FRqZnxI5w==
-----END CERTIFICATE REQUEST-----
principal: pinky
profile_id: IECUserRoles
state: requested
- name: Temporarily hold a certificate
ipacert:
ipaadmin_password: SomeADMINpassword
serial_number: 12345
state: held
- name: Remove a certificate hold
ipacert:
ipaadmin_password: SomeADMINpassword
state: released
serial_number: 12345
- name: Permanently revoke a certificate issued by a lightweight sub-CA
ipacert:
ipaadmin_password: SomeADMINpassword
state: revoked
ca: vpn-ca
serial_number: 0x98765
reason: keyCompromise
- name: Retrieve a certificate
ipacert:
ipaadmin_password: SomeADMINpassword
serial_number: 12345
state: retrieved
register: cert_retrieved
"""
RETURN = """
certificate:
description: Certificate fields and data.
returned: |
if `state` is `requested` or `retrived` and `certificate_out`
is not defined.
type: dict
contains:
certificate:
description: |
Issued X509 certificate in PEM encoding. Will include certificate
chain if `chain: true` is used.
type: list
elements: str
returned: always
issuer:
description: X509 distinguished name of issuer.
type: str
sample: CN=Certificate Authority,O=EXAMPLE.COM
returned: always
serial_number:
description: Serial number of the issued certificate.
type: int
sample: 902156300
returned: always
valid_not_after:
description: |
Time when issued certificate ceases to be valid,
in GeneralizedTime format (YYYYMMDDHHMMSSZ).
type: str
returned: always
valid_not_before:
description: |
Time when issued certificate becomes valid, in
GeneralizedTime format (YYYYMMDDHHMMSSZ).
type: str
returned: always
subject:
description: X509 distinguished name of certificate subject.
type: str
sample: CN=www.example.com,O=EXAMPLE.COM
returned: always
san_dnsname:
description: X509 Subject Alternative Name.
type: list
elements: str
sample: ['www.example.com', 'other.example.com']
returned: |
when DNSNames are present in the Subject Alternative Name
extension of the issued certificate.
revoked:
description: Revoked status of the certificate.
type: bool
returned: always
owner_user:
description: The username that owns the certificate.
type: str
returned: when `state` is `retrieved`
owner_host:
description: The host that owns the certificate.
type: str
returned: when `state` is `retrieved`
owner_service:
description: The service that owns the certificate.
type: str
returned: when `state` is `retrieved`
"""
import base64
import time
import ssl
from ansible.module_utils import six
from ansible.module_utils._text import to_text
from ansible.module_utils.ansible_freeipa_module import (
IPAAnsibleModule, certificate_loader, write_certificate_list,
)
if six.PY3:
unicode = str
# Reasons are defined in RFC 5280 sec. 5.3.1; removeFromCRL is not present in
# this list; run the module with state=released instead.
REVOCATION_REASONS = {
'unspecified': 0,
'keyCompromise': 1,
'cACompromise': 2,
'affiliationChanged': 3,
'superseded': 4,
'cessationOfOperation': 5,
'certificateHold': 6,
'removeFromCRL': 8,
'privilegeWithdrawn': 9,
'aACompromise': 10,
}
def gen_args(
module, principal=None, add_principal=None, ca=None, chain=None,
profile=None, certificate_out=None, reason=None
):
args = {}
if principal is not None:
args['principal'] = principal
if add_principal is not None:
args['add'] = add_principal
if ca is not None:
args['cacn'] = ca
if profile is not None:
args['profile_id'] = profile
if certificate_out is not None:
args['out'] = certificate_out
if chain:
args['chain'] = True
if ca:
args['cacn'] = ca
if reason is not None:
args['revocation_reason'] = get_revocation_reason(module, reason)
return args
def get_revocation_reason(module, reason):
"""Ensure revocation reasion is a valid integer code."""
reason_int = -1
try:
reason_int = int(reason)
except ValueError:
reason_int = REVOCATION_REASONS.get(reason, -1)
if reason_int not in REVOCATION_REASONS.values():
module.fail_json(msg="Invalid revocation reason: %s" % reason)
return reason_int
def parse_cert_timestamp(dt):
"""Ensure time is in GeneralizedTime format (YYYYMMDDHHMMSSZ)."""
return time.strftime(
"%Y%m%d%H%M%SZ",
time.strptime(dt, "%a %b %d %H:%M:%S %Y UTC")
)
def result_handler(_module, result, _command, _name, _args, exit_args, chain):
"""Split certificate into fields."""
if chain:
exit_args['certificate'] = [
ssl.DER_cert_to_PEM_cert(c)
for c in result['result'].get('certificate_chain', [])
]
else:
exit_args['certificate'] = [
ssl.DER_cert_to_PEM_cert(
base64.b64decode(result['result']['certificate'])
)
]
exit_args['san_dnsname'] = [
str(dnsname)
for dnsname in result['result'].get('san_dnsname', [])
]
exit_args.update({
key: result['result'][key]
for key in [
'issuer', 'subject', 'serial_number',
'revoked', 'revocation_reason'
]
if key in result['result']
})
exit_args.update({
key: result['result'][key][0]
for key in ['owner_user', 'owner_host', 'owner_service']
if key in result['result']
})
exit_args.update({
key: parse_cert_timestamp(result['result'][key])
for key in ['valid_not_after', 'valid_not_before']
if key in result['result']
})
def do_cert_request(
module, csr, principal, add_principal=None, ca=None, profile=None,
chain=None, certificate_out=None
):
"""Request a certificate."""
args = gen_args(
module, principal=principal, ca=ca, chain=chain,
add_principal=add_principal, profile=profile,
)
exit_args = {}
commands = [[to_text(csr), "cert_request", args]]
changed = module.execute_ipa_commands(
commands,
result_handler=result_handler,
exit_args=exit_args,
chain=chain
)
if certificate_out is not None:
certs = (
certificate_loader(cert.encode("utf-8"))
for cert in exit_args['certificate']
)
write_certificate_list(certs, certificate_out)
exit_args = {}
return changed, exit_args
def do_cert_revoke(ansible_module, serial_number, reason=None, ca=None):
"""Revoke a certificate."""
_ign, cert = do_cert_retrieve(ansible_module, serial_number, ca)
if not cert or cert.get('revoked', False):
return False, cert
args = gen_args(ansible_module, ca=ca, reason=reason)
commands = [[serial_number, "cert_revoke", args]]
changed = ansible_module.execute_ipa_commands(commands)
return changed, cert
def do_cert_release(ansible_module, serial_number, ca=None):
"""Release hold on certificate."""
_ign, cert = do_cert_retrieve(ansible_module, serial_number, ca)
revoked = cert.get('revoked', True)
reason = cert.get('revocation_reason', -1)
if cert and not revoked:
return False, cert
if revoked and reason != 6: # can only release held certificates
ansible_module.fail_json(
msg="Cannot release hold on certificate revoked with"
" reason: %d" % reason
)
args = gen_args(ansible_module, ca=ca)
commands = [[serial_number, "cert_remove_hold", args]]
changed = ansible_module.execute_ipa_commands(commands)
return changed, cert
def do_cert_retrieve(
module, serial_number, ca=None, chain=None, outfile=None
):
"""Retrieve a certificate with 'cert-show'."""
args = gen_args(module, ca=ca, chain=chain, certificate_out=outfile)
exit_args = {}
commands = [[serial_number, "cert_show", args]]
module.execute_ipa_commands(
commands,
result_handler=result_handler,
exit_args=exit_args,
chain=chain,
)
if outfile is not None:
exit_args = {}
return False, exit_args
def main():
ansible_module = IPAAnsibleModule(
argument_spec=dict(
# requested
csr=dict(type="str"),
csr_file=dict(type="str"),
principal=dict(type="str"),
add_principal=dict(type="bool", required=False, aliases=["add"]),
profile_id=dict(type="str", aliases=["profile"], required=False),
# revoked
revocation_reason=dict(type="raw", aliases=["reason"]),
# general
serial_number=dict(type="int"),
ca=dict(type="str"),
chain=dict(type="bool", required=False),
certificate_out=dict(type="str", required=False),
# state
state=dict(
type="str",
required=True,
choices=[
"requested", "held", "released", "revoked", "retrieved"
]
),
),
mutually_exclusive=[["csr", "csr_file"]],
required_if=[
('state', 'requested', ['principal']),
('state', 'retrieved', ['serial_number']),
('state', 'held', ['serial_number']),
('state', 'released', ['serial_number']),
('state', 'revoked', ['serial_number', 'revocation_reason']),
],
supports_check_mode=False,
)
ansible_module._ansible_debug = True
# Get parameters
# requested
csr = ansible_module.params_get("csr")
csr_file = ansible_module.params_get("csr_file")
principal = ansible_module.params_get("principal")
add_principal = ansible_module.params_get("add_principal")
profile = ansible_module.params_get("profile_id")
# revoked
reason = ansible_module.params_get("revocation_reason")
# general
serial_number = ansible_module.params.get("serial_number")
ca = ansible_module.params_get("ca")
chain = ansible_module.params_get("chain")
certificate_out = ansible_module.params_get("certificate_out")
# state
state = ansible_module.params_get("state")
# Check parameters
if ansible_module.params_get("ipaapi_context") == "server":
ansible_module.fail_json(
msg="Context 'server' for ipacert is not yet supported."
)
invalid = []
if state == "requested":
invalid = ["serial_number", "revocation_reason"]
if csr is None and csr_file is None:
ansible_module.fail_json(
msg="Required 'csr' or 'csr_file' with 'state: requested'.")
else:
invalid = [
"csr", "principal", "add_principal", "profile"
"certificate_out"
]
if state in ["released", "held"]:
invalid.extend(["revocation_reason", "certificate_out", "chain"])
if state == "retrieved":
invalid.append("revocation_reason")
if state == "revoked":
invalid.extend(["certificate_out", "chain"])
elif state == "held":
reason = 6 # certificateHold
ansible_module.params_fail_used_invalid(invalid, state)
# Init
changed = False
exit_args = {}
# Connect to IPA API
# If executed on 'server' contexot, cert plugin uses the IPA RA agent
# TLS client certificate/key, which users are not able to access,
# resulting in a 'permission denied' exception when attempting to connect
# the CA service. Therefore 'client' context in forced for this module.
with ansible_module.ipa_connect(context="client"):
if state == "requested":
if csr_file is not None:
with open(csr_file, "rt") as csr_in:
csr = "".join(csr_in.readlines())
changed, exit_args = do_cert_request(
ansible_module,
csr,
principal,
add_principal,
ca,
profile,
chain,
certificate_out
)
elif state in ("held", "revoked"):
changed, exit_args = do_cert_revoke(
ansible_module, serial_number, reason, ca)
elif state == "released":
changed, exit_args = do_cert_release(
ansible_module, serial_number, ca)
elif state == "retrieved":
changed, exit_args = do_cert_retrieve(
ansible_module, serial_number, ca, chain, certificate_out)
# Done
ansible_module.exit_json(changed=changed, certificate=exit_args)
if __name__ == "__main__":
main()

View File

@@ -160,7 +160,8 @@ options:
required: false
type: list
elements: str
choices: ["password", "radius", "otp", "disabled", ""]
choices: ["password", "radius", "otp", "pkinit", "hardened", "idp",
"disabled", ""]
aliases: ["ipauserauthtype"]
ca_renewal_master_server:
description: Renewal master for IPA certificate authority.
@@ -357,8 +358,7 @@ def get_netbios_name(module):
_result = module.ipa_command_no_name("trustconfig_show", {"all": True})
except Exception: # pylint: disable=broad-except
return None
else:
return _result["result"]["ipantflatname"][0]
return _result["result"]["ipantflatname"][0]
def is_enable_sid(module):
@@ -425,6 +425,7 @@ def main():
choices=["MS-PAC", "PAD", "nfs:NONE", ""]),
user_auth_type=dict(type="list", elements="str", required=False,
choices=["password", "radius", "otp",
"pkinit", "hardened", "idp",
"disabled", ""],
aliases=["ipauserauthtype"]),
ca_renewal_master_server=dict(type="str", required=False),
@@ -469,13 +470,13 @@ def main():
"netbios_name": "netbios_name",
"add_sids": "add_sids",
}
allow_empty_string = ["pac_type", "user_auth_type", "configstring"]
reverse_field_map = {v: k for k, v in field_map.items()}
allow_empty_list_item = ["pac_type", "user_auth_type", "configstring"]
params = {}
for x in field_map:
val = ansible_module.params_get(
x, allow_empty_string=(x in allow_empty_string))
x, allow_empty_list_item=x in allow_empty_list_item)
if val is not None:
params[field_map.get(x, x)] = val
@@ -525,6 +526,15 @@ def main():
result = config_show(ansible_module)
if params:
# Verify ipauserauthtype(s)
if "ipauserauthtype" in params and params["ipauserauthtype"]:
_invalid = ansible_module.ipa_command_invalid_param_choices(
"config_mod", "ipauserauthtype", params["ipauserauthtype"])
if _invalid:
ansible_module.fail_json(
msg="The use of userauthtype '%s' is not "
"supported by your IPA version" % "','".join(_invalid))
enable_sid = params.get("enable_sid")
sid_is_enabled = has_enable_sid and is_enable_sid(ansible_module)
@@ -609,7 +619,7 @@ def main():
# boolean values, so we need to convert it to str
# for comparison.
# See: https://github.com/freeipa/freeipa/pull/6294
exit_args[k] = (str(value[0]).upper() == "TRUE")
exit_args[k] = str(value[0]).upper() == "TRUE"
else:
if arg_type not in type_map:
raise ValueError(

View File

@@ -134,8 +134,7 @@ def find_delegation(module, name):
except Exception: # pylint: disable=broad-except
# An exception is raised if delegation name is not found.
return None
else:
return _result["result"]
return _result["result"]
def gen_args(permission, attribute, membergroup, group):
@@ -181,10 +180,10 @@ def main():
names = ansible_module.params_get("name")
# present
permission = ansible_module.params_get("permission")
attribute = ansible_module.params_get("attribute")
permission = ansible_module.params_get_lowercase("permission")
attribute = ansible_module.params_get_lowercase("attribute")
membergroup = ansible_module.params_get("membergroup")
group = ansible_module.params_get("group")
group = ansible_module.params_get_lowercase("group")
action = ansible_module.params_get("action")
# state
state = ansible_module.params_get("state")
@@ -234,6 +233,7 @@ def main():
commands = []
for name in names:
args = {}
# Make sure delegation exists
res_find = find_delegation(ansible_module, name)
@@ -245,14 +245,7 @@ def main():
if action == "delegation":
# Found the delegation
if res_find is not None:
# For all settings is args, check if there are
# different settings in the find result.
# If yes: modify
if not compare_args_ipa(ansible_module, args,
res_find):
commands.append([name, "delegation_mod", args])
else:
if res_find is None:
commands.append([name, "delegation_add", args])
elif action == "member":
@@ -266,9 +259,7 @@ def main():
# New attribute list (add given ones to find result)
# Make list with unique entries
attrs = list(set(list(res_find["attrs"]) + attribute))
if len(attrs) > len(res_find["attrs"]):
commands.append([name, "delegation_mod",
{"attrs": attrs}])
args["attrs"] = attrs
elif state == "absent":
if action == "delegation":
@@ -289,15 +280,18 @@ def main():
if len(attrs) < 1:
ansible_module.fail_json(
msg="At minimum one attribute is needed.")
# Entries New number of attributes is smaller
if len(attrs) < len(res_find["attrs"]):
commands.append([name, "delegation_mod",
{"attrs": attrs}])
args["attrs"] = attrs
else:
ansible_module.fail_json(msg="Unkown state '%s'" % state)
# Manage members
if (
args and res_find and
not compare_args_ipa(ansible_module, args, res_find)
):
commands.append([name, "delegation_mod", args])
# Execute commands
changed = ansible_module.execute_ipa_commands(commands)

View File

@@ -250,6 +250,8 @@ def main():
operation = "add"
invalid = []
wants_enable = False
if state in ["enabled", "disabled"]:
if action == "member":
ansible_module.fail_json(
@@ -258,7 +260,7 @@ def main():
invalid = [
"forwarders", "forwardpolicy", "skip_overlap_check", "permission"
]
wants_enable = (state == "enabled")
wants_enable = state == "enabled"
if operation == "del":
invalid = [

View File

@@ -1394,15 +1394,16 @@ def gen_args(entry):
if record_value is not None:
record_type = entry['record_type']
rec = "{}record".format(record_type.lower())
rec = "{0}record".format(record_type.lower())
args[rec] = ensure_data_is_list(record_value)
else:
for field in _RECORD_FIELDS:
record_value = entry.get(field) or entry.get("%sord" % field)
if record_value is not None:
# pylint: disable=use-maxsplit-arg
record_type = field.split('_')[0]
rec = "{}record".format(record_type.lower())
rec = "{0}record".format(record_type.lower())
args[rec] = ensure_data_is_list(record_value)
records = {
@@ -1452,7 +1453,7 @@ def define_commands_for_present_state(module, zone_name, entry, res_find):
else:
# Create reverse records for existing records
for ipv in ['a', 'aaaa']:
record = ('%srecord' % ipv)
record = '%srecord' % ipv
if record in args and ('%s_extra_create_reverse' % ipv) in args:
cmds = create_reverse_ip_record(
module, zone_name, name, args[record])
@@ -1604,6 +1605,8 @@ def main():
res_find = find_dnsrecord(ansible_module, zone_name, name)
cmds = []
if state == 'present':
cmds = define_commands_for_present_state(
ansible_module, zone_name, entry, res_find)

View File

@@ -142,6 +142,11 @@ options:
salt.
required: false
type: str
permission:
description: Set per-zone access delegation permission.
required: false
type: bool
aliases: ["managedby"]
skip_overlap_check:
description: |
Force DNS zone creation even if it will overlap with an existing zone
@@ -154,6 +159,7 @@ options:
author:
- Sergio Oliveira Campos (@seocam)
- Thomas Woerner (@t-woerner)
- Rafael Jeffman (@rjeffman)
""" # noqa: E501
EXAMPLES = """
@@ -253,6 +259,9 @@ class DNSZoneModule(IPAAnsibleModule):
"idnsallowdynupdate": "dynamic_update",
"idnssecinlinesigning": "dnssec",
"idnsupdatepolicy": "update_policy",
# FreeIPA uses 'managedby' for dnszone and dnsforwardzone
# to manage 'permissions'.
"managedby": "permission",
# Mapping by method
"idnsforwarders": self.get_ipa_idnsforwarders,
"idnsallowtransfer": self.get_ipa_idnsallowtransfer,
@@ -434,7 +443,7 @@ class DNSZoneModule(IPAAnsibleModule):
is_zone_active = False
else:
zone = response["result"]
# FreeIPA 4.9.10+ and 4.10 use proper mapping for boolean vaalues.
# FreeIPA 4.9.10+ and 4.10 use proper mapping for boolean values.
# See: https://github.com/freeipa/freeipa/pull/6294
is_zone_active = (
str(zone.get("idnszoneactive")[0]).upper() == "TRUE"
@@ -462,18 +471,24 @@ class DNSZoneModule(IPAAnsibleModule):
self.fail_json(
msg="Either `name` or `name_from_ip` must be provided."
)
# check invalid parameters
invalid = []
if self.ipa_params.state != "present":
invalid = ["name_from_ip"]
self.params_fail_used_invalid(invalid, self.ipa_params.state)
invalid .extend(["name_from_ip"])
if self.ipa_params.state == "absent":
invalid.extend(["permission"])
self.params_fail_used_invalid(invalid, self.ipa_params.state)
def define_ipa_commands(self):
for zone_name in self.get_zone_names():
# Look for existing zone in IPA
zone, is_zone_active = self.get_zone(zone_name)
args = self.ipa_params.get_ipa_command_args(zone=zone)
if self.ipa_params.state in ["present", "enabled", "disabled"]:
args = self.ipa_params.get_ipa_command_args(zone=zone)
# We'll handle "managedby" after dnszone add/mod.
args.pop("managedby", None)
if not zone:
# Since the zone doesn't exist we just create it
# with given args
@@ -487,6 +502,16 @@ class DNSZoneModule(IPAAnsibleModule):
if not compare_args_ipa(self, args, zone):
self.commands.append((zone_name, "dnszone_mod", args))
# Permissions must be set on existing zones.
if self.ipa_params.permission is not None:
is_managed = zone.get("managedby")
if self.ipa_params.permission and not is_managed:
self.commands.append(
(zone_name, "dnszone_add_permission", {}))
if not self.ipa_params.permission and is_managed:
self.commands.append(
(zone_name, "dnszone_remove_permission", {}))
if self.ipa_params.state == "enabled" and not is_zone_active:
self.commands.append((zone_name, "dnszone_enable", {}))
@@ -555,6 +580,8 @@ def get_argument_spec():
ttl=dict(type="int", required=False, default=None),
default_ttl=dict(type="int", required=False, default=None),
nsec3param_rec=dict(type="str", required=False, default=None),
permission=dict(type="bool", required=False, default=None,
aliases=["managedby"]),
skip_nameserver_check=dict(type="bool", required=False, default=None),
skip_overlap_check=dict(type="bool", required=False, default=None),
)

View File

@@ -41,8 +41,93 @@ 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
rename:
description: Rename the group object
required: false
type: str
aliases: ["new_name"]
description:
description: The group description
type: str
@@ -118,11 +203,16 @@ options:
type: str
default: group
choices: ["member", "group"]
rename:
description: Rename the group object
required: false
type: str
aliases: ["new_name"]
state:
description: State to ensure
type: str
default: present
choices: ["present", "absent"]
choices: ["present", "absent", "renamed"]
author:
- Thomas Woerner (@t-woerner)
"""
@@ -144,6 +234,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 +258,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 +266,24 @@ 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
# Rename a group
- ipagroup:
ipaadmin_password: SomeADMINpassword
name: oldname
rename: newestname
state: renamed
# Create a non-POSIX group
- ipagroup:
ipaadmin_password: SomeADMINpassword
@@ -189,7 +305,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 +328,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 +396,24 @@ def gen_member_args(user, group, service, externalmember, idoverrideuser):
return _args
def check_parameters(module, state, action):
invalid = ["description", "gid", "posix", "nonposix", "external",
"nomembers"]
if action == "group":
if state == "present":
invalid = []
elif state == "absent":
invalid.extend(["user", "group", "service", "externalmember"])
if state == "renamed":
if action == "member":
module.fail_json(
msg="Action member can not be used with state: renamed.")
invalid.extend(["user", "group", "service", "externalmember"])
else:
invalid.append("rename")
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 +442,65 @@ 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"
]),
rename=dict(type="str", required=False, default=None,
aliases=["new_name"]),
)
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"]),
choices=["present", "absent", "renamed"]),
# 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 +510,7 @@ def main():
# general
names = ansible_module.params_get("name")
groups = ansible_module.params_get("groups")
# present
description = ansible_module.params_get("description")
@@ -342,43 +520,67 @@ def main():
idoverrideuser = ansible_module.params_get("idoverrideuser")
posix = ansible_module.params_get("posix")
nomembers = ansible_module.params_get("nomembers")
user = ansible_module.params_get("user")
group = ansible_module.params_get("group")
user = ansible_module.params_get_lowercase("user")
group = ansible_module.params_get_lowercase("group")
# Services are not case sensitive
service = ansible_module.params_get_lowercase("service")
membermanager_user = ansible_module.params_get("membermanager_user")
membermanager_group = ansible_module.params_get("membermanager_group")
membermanager_user = (
ansible_module.params_get_lowercase("membermanager_user"))
membermanager_group = (
ansible_module.params_get_lowercase("membermanager_group"))
externalmember = ansible_module.params_get("externalmember")
# rename
rename = ansible_module.params_get("rename")
# state and action
action = ansible_module.params_get("action")
# state
state = ansible_module.params_get("state")
# Check parameters
invalid = []
if state == "present":
if len(names) != 1:
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 in ["present", "renamed"]:
if names is not None and len(names) != 1:
what = "renamed" if state == "renamed" else "added"
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 %s at a time using 'name'." % what)
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 +591,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 +617,62 @@ 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")
rename = group_name.get("rename")
check_parameters(ansible_module, state, action)
elif (
isinstance(
group_name, (str, unicode) # pylint: disable=W0012,E0606
)
):
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)
@@ -569,6 +825,11 @@ def main():
membermanager_group,
res_find.get("membermanager_group")
)
elif state == "renamed":
if res_find is None:
ansible_module.fail_json(msg="No group '%s'" % name)
elif rename != name:
commands.append([name, 'group_mod', {"rename": rename}])
else:
ansible_module.fail_json(msg="Unkown state '%s'" % state)
@@ -593,10 +854,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 "
@@ -641,7 +904,7 @@ def main():
# Execute commands
changed = ansible_module.execute_ipa_commands(
commands, fail_on_member_errors=True)
commands, batch=True, keeponly=[], fail_on_member_errors=True)
# Done

View File

@@ -186,7 +186,16 @@ def find_hbacrule(module, name):
module.fail_json(
msg="There is more than one hbacrule '%s'" % (name))
elif len(_result["result"]) == 1:
return _result["result"][0]
res = _result["result"][0]
# hbacsvcgroup names are converted to lower case while creation with
# hbacsvcgroup_add, but builtin names may have mixed case as "Sudo",
# breaking the lower case comparison. Therefore all
# memberservice_hbacsvcgroup items are converted to lower case.
# (See: https://pagure.io/freeipa/issue/9464).
_member = "memberservice_hbacsvcgroup"
if _member in res:
res[_member] = [item.lower() for item in res[_member]]
return res
return None
@@ -390,7 +399,8 @@ def main():
if hbacsvc is not None:
hbacsvc_add, hbacsvc_del = gen_add_del_lists(
hbacsvc, res_find.get("memberservice_hbacsvc"))
hbacsvc, res_find.get("memberservice_hbacsvc"),
)
if hbacsvcgroup is not None:
hbacsvcgroup_add, hbacsvcgroup_del = gen_add_del_lists(

View File

@@ -146,21 +146,6 @@ def gen_member_args(hbacsvc):
return _args
# pylint: disable=unused-argument
def result_handler(module, result, command, name, args, errors):
# Get all errors
# All "already a member" and "not a member" failures in the
# result are ignored. All others are reported.
if "failed" in result and "member" in result["failed"]:
failed = result["failed"]["member"]
for member_type in failed:
for member, failure in failed[member_type]:
if "already a member" not in failure \
and "not a member" not in failure:
errors.append("%s: %s %s: %s" % (
command, member_type, member, failure))
def main():
ansible_module = IPAAnsibleModule(
argument_spec=dict(
@@ -303,7 +288,8 @@ def main():
}])
# Execute commands
changed = ansible_module.execute_ipa_commands(commands, result_handler)
changed = ansible_module.execute_ipa_commands(
commands, fail_on_member_errors=True)
# Done

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
@@ -63,7 +63,7 @@ options:
type: str
required: false
location:
description: Host location (e.g. "Lab 2")
description: Host physical location hist (e.g. "Lab 2")
type: str
aliases: ["ns_host_location"]
required: false
@@ -184,7 +184,7 @@ options:
type: list
elements: str
aliases: ["krbprincipalauthind"]
choices: ["radius", "otp", "pkinit", "hardened", ""]
choices: ["radius", "otp", "pkinit", "hardened", "idp", ""]
required: false
requires_pre_auth:
description: Pre-authentication is required for the service
@@ -356,7 +356,7 @@ options:
type: list
elements: str
aliases: ["krbprincipalauthind"]
choices: ["radius", "otp", "pkinit", "hardened", ""]
choices: ["radius", "otp", "pkinit", "hardened", "idp", ""]
required: false
requires_pre_auth:
description: Pre-authentication is required for the service
@@ -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:
@@ -486,7 +509,9 @@ host:
from ansible.module_utils.ansible_freeipa_module import \
IPAAnsibleModule, compare_args_ipa, gen_add_del_lists, \
encode_certificate, is_ipv4_addr, is_ipv6_addr, ipalib_errors
encode_certificate, is_ipv4_addr, is_ipv6_addr, ipalib_errors, \
gen_add_list, gen_intersection_list, normalize_sshpubkey, \
convert_input_certificates
from ansible.module_utils import six
if six.PY3:
unicode = str
@@ -510,6 +535,11 @@ def find_host(module, name):
if certs is not None:
_res["usercertificate"] = [encode_certificate(cert) for
cert in certs]
# krbprincipalname is returned as ipapython.kerberos.Principal, convert
# to string
principals = _res.get("krbprincipalname")
if principals is not None:
_res["krbprincipalname"] = [str(princ) for princ in principals]
return _res
@@ -644,53 +674,27 @@ def check_parameters( # pylint: disable=unused-argument
module.params_fail_used_invalid(invalid, state, action)
def check_authind(module, auth_ind):
_invalid = module.ipa_command_invalid_param_choices(
"host_add", "krbprincipalauthind", auth_ind)
if _invalid:
module.fail_json(
msg="The use of krbprincipalauthind '%s' is not supported "
"by your IPA version" % "','".join(_invalid))
# pylint: disable=unused-argument
def result_handler(module, result, command, name, args, errors, exit_args,
one_name):
def result_handler(module, result, command, name, args, exit_args,
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:
exit_args.setdefault(name, {})["randompassword"] = \
result["result"]["randompassword"]
# All "already a member" and "not a member" failures in the
# result are ignored. All others are reported.
if "failed" in result and len(result["failed"]) > 0:
for item in result["failed"]:
failed_item = result["failed"][item]
for member_type in failed_item:
for member, failure in failed_item[member_type]:
if "already a member" in failure \
or "not a member" in failure:
continue
errors.append("%s: %s %s: %s" % (
command, member_type, member, failure))
# pylint: disable=unused-argument
def exception_handler(module, ex, errors, exit_args, one_name):
msg = str(ex)
if "already contains" in msg \
or "does not contain" in msg:
return True
# The canonical principal name may not be removed
if "equal to the canonical principal name must" in msg:
return True
# Host is already disabled, ignore error
if "This entry is already disabled" in msg:
return True
# Ignore no modification error.
if "no modifications to be performed" in msg:
return True
return False
def main():
host_spec = dict(
@@ -753,7 +757,8 @@ def main():
default=None),
auth_ind=dict(type='list', elements="str",
aliases=["krbprincipalauthind"], default=None,
choices=['radius', 'otp', 'pkinit', 'hardened', '']),
choices=["radius", "otp", "pkinit", "hardened", "idp",
""]),
requires_pre_auth=dict(type="bool", aliases=["ipakrbrequirespreauth"],
default=None),
ok_as_delegate=dict(type="bool", aliases=["ipakrbokasdelegate"],
@@ -843,10 +848,11 @@ def main():
allow_retrieve_keytab_hostgroup = ansible_module.params_get(
"allow_retrieve_keytab_hostgroup")
mac_address = ansible_module.params_get("mac_address")
sshpubkey = ansible_module.params_get("sshpubkey",
allow_empty_string=True)
sshpubkey = ansible_module.params_get(
"sshpubkey", allow_empty_list_item=True)
userclass = ansible_module.params_get("userclass")
auth_ind = ansible_module.params_get("auth_ind", allow_empty_string=True)
auth_ind = ansible_module.params_get(
"auth_ind", allow_empty_list_item=True)
requires_pre_auth = ansible_module.params_get("requires_pre_auth")
ok_as_delegate = ansible_module.params_get("ok_as_delegate")
ok_to_auth_as_delegate = ansible_module.params_get(
@@ -882,6 +888,12 @@ def main():
auth_ind, requires_pre_auth, ok_as_delegate, ok_to_auth_as_delegate,
force, reverse, ip_address, update_dns, update_password)
certificate = convert_input_certificates(ansible_module, certificate,
state)
if sshpubkey is not None:
sshpubkey = [str(normalize_sshpubkey(key)) for key in sshpubkey]
# Use hosts if names is None
if hosts is not None:
names = hosts
@@ -896,6 +908,8 @@ def main():
# Check version specific settings
check_authind(ansible_module, auth_ind)
server_realm = ansible_module.ipa_get_realm()
commands = []
@@ -938,6 +952,7 @@ def main():
sshpubkey = host.get("sshpubkey")
userclass = host.get("userclass")
auth_ind = host.get("auth_ind")
check_authind(ansible_module, auth_ind)
requires_pre_auth = host.get("requires_pre_auth")
ok_as_delegate = host.get("ok_as_delegate")
ok_to_auth_as_delegate = host.get("ok_to_auth_as_delegate")
@@ -962,7 +977,16 @@ def main():
ok_to_auth_as_delegate, force, reverse, ip_address,
update_dns, update_password)
elif isinstance(host, (str, unicode)):
certificate = convert_input_certificates(ansible_module,
certificate, state)
if sshpubkey is not None:
sshpubkey = [str(normalize_sshpubkey(key)) for
key in sshpubkey]
elif (
isinstance(host, (str, unicode)) # pylint: disable=W0012,E0606
):
name = host
else:
ansible_module.fail_json(msg="Host '%s' is not valid" %
@@ -1037,6 +1061,17 @@ def main():
args["krbprincipalauthind"] == ['']:
del args["krbprincipalauthind"]
# Ignore sshpubkey if it is empty (for resetting)
# and not set for the host
if "ipasshpubkey" not in res_find and \
"ipasshpubkey" in args and \
args["ipasshpubkey"] == []:
del args["ipasshpubkey"]
# Ignore updatedns if it is the only arg
if "updatedns" in args and len(args) == 1:
del args["updatedns"]
# For all settings is args, check if there are
# different settings in the find result.
# If yes: modify
@@ -1069,7 +1104,7 @@ def main():
gen_add_del_lists(managedby_host,
res_find.get("managedby_host"))
principal_add, principal_del = gen_add_del_lists(
principal, res_find.get("principal"))
principal, res_find.get("krbprincipalname"))
# Principals are not returned as utf8 for IPA using
# python2 using host_show, therefore we need to
# convert the principals that we should remove.
@@ -1137,50 +1172,115 @@ def main():
gen_add_del_lists(
dnsrecord_args.get("aaaarecord"),
_dnsrec.get("aaaarecord"))
else:
certificate_add = certificate or []
certificate_del = []
managedby_host_add = managedby_host or []
managedby_host_del = []
principal_add = principal or []
principal_del = []
allow_create_keytab_user_add = \
allow_create_keytab_user or []
allow_create_keytab_user_del = []
allow_create_keytab_group_add = \
allow_create_keytab_group or []
allow_create_keytab_group_del = []
allow_create_keytab_host_add = \
allow_create_keytab_host or []
allow_create_keytab_host_del = []
allow_create_keytab_hostgroup_add = \
allow_create_keytab_hostgroup or []
allow_create_keytab_hostgroup_del = []
allow_retrieve_keytab_user_add = \
allow_retrieve_keytab_user or []
allow_retrieve_keytab_user_del = []
allow_retrieve_keytab_group_add = \
allow_retrieve_keytab_group or []
allow_retrieve_keytab_group_del = []
allow_retrieve_keytab_host_add = \
allow_retrieve_keytab_host or []
allow_retrieve_keytab_host_del = []
allow_retrieve_keytab_hostgroup_add = \
allow_retrieve_keytab_hostgroup or []
allow_retrieve_keytab_hostgroup_del = []
_dnsrec = res_find_dnsrecord or {}
dnsrecord_a_add = gen_add_list(
dnsrecord_args.get("arecord"),
_dnsrec.get("arecord"))
dnsrecord_a_del = []
dnsrecord_aaaa_add = gen_add_list(
dnsrecord_args.get("aaaarecord"),
_dnsrec.get("aaaarecord"))
dnsrecord_aaaa_del = []
else:
# action member
if res_find is None:
ansible_module.fail_json(
msg="No host '%s'" % name)
if action != "host" or (action == "host" and res_find is None):
certificate_add = certificate or []
certificate_add = gen_add_list(
certificate, res_find.get("usercertificate"))
certificate_del = []
managedby_host_add = managedby_host or []
managedby_host_add = gen_add_list(
managedby_host, res_find.get("managedby_host"))
managedby_host_del = []
principal_add = principal or []
principal_add = gen_add_list(
principal, res_find.get("krbprincipalname"))
principal_del = []
allow_create_keytab_user_add = \
allow_create_keytab_user or []
allow_create_keytab_user_add = gen_add_list(
allow_create_keytab_user,
res_find.get(
"ipaallowedtoperform_write_keys_user"))
allow_create_keytab_user_del = []
allow_create_keytab_group_add = \
allow_create_keytab_group or []
allow_create_keytab_group_add = gen_add_list(
allow_create_keytab_group,
res_find.get(
"ipaallowedtoperform_write_keys_group"))
allow_create_keytab_group_del = []
allow_create_keytab_host_add = \
allow_create_keytab_host or []
allow_create_keytab_host_add = gen_add_list(
allow_create_keytab_host,
res_find.get(
"ipaallowedtoperform_write_keys_host"))
allow_create_keytab_host_del = []
allow_create_keytab_hostgroup_add = \
allow_create_keytab_hostgroup or []
allow_create_keytab_hostgroup_add = gen_add_list(
allow_create_keytab_hostgroup,
res_find.get(
"ipaallowedtoperform_write_keys_hostgroup"))
allow_create_keytab_hostgroup_del = []
allow_retrieve_keytab_user_add = \
allow_retrieve_keytab_user or []
allow_retrieve_keytab_user_add = gen_add_list(
allow_retrieve_keytab_user,
res_find.get(
"ipaallowedtoperform_read_keys_user"))
allow_retrieve_keytab_user_del = []
allow_retrieve_keytab_group_add = \
allow_retrieve_keytab_group or []
allow_retrieve_keytab_group_add = gen_add_list(
allow_retrieve_keytab_group,
res_find.get(
"ipaallowedtoperform_read_keys_group"))
allow_retrieve_keytab_group_del = []
allow_retrieve_keytab_host_add = \
allow_retrieve_keytab_host or []
allow_retrieve_keytab_host_add = gen_add_list(
allow_retrieve_keytab_host,
res_find.get(
"ipaallowedtoperform_read_keys_host"))
allow_retrieve_keytab_host_del = []
allow_retrieve_keytab_hostgroup_add = \
allow_retrieve_keytab_hostgroup or []
allow_retrieve_keytab_hostgroup_add = gen_add_list(
allow_retrieve_keytab_hostgroup,
res_find.get(
"ipaallowedtoperform_read_keys_hostgroup"))
allow_retrieve_keytab_hostgroup_del = []
dnsrecord_a_add = dnsrecord_args.get("arecord") or []
_dnsrec = res_find_dnsrecord or {}
dnsrecord_a_add = gen_add_list(
dnsrecord_args.get("arecord"),
_dnsrec.get("arecord"))
dnsrecord_a_del = []
dnsrecord_aaaa_add = dnsrecord_args.get("aaaarecord") or []
dnsrecord_aaaa_add = gen_add_list(
dnsrecord_args.get("aaaarecord"),
_dnsrec.get("aaaarecord"))
dnsrecord_aaaa_del = []
# Remove canonical principal from principal_del
canonical_principal = "host/" + name + "@" + server_realm
# canonical_principal is also in find_res["krbcanonicalname"]
if canonical_principal in principal_del and \
action == "host" and (principal is not None or
canonical_principal not in principal):
@@ -1361,8 +1461,10 @@ def main():
# the removal of non-existing entries.
# Remove certificates
if certificate is not None:
for _certificate in certificate:
certificate_del = gen_intersection_list(
certificate, res_find.get("usercertificate"))
if certificate_del is not None:
for _certificate in certificate_del:
commands.append([name, "host_remove_cert",
{
"usercertificate":
@@ -1375,8 +1477,10 @@ def main():
# the removal of non-existing entries.
# Remove managedby_hosts
if managedby_host is not None:
for _managedby_host in managedby_host:
managedby_host_del = gen_intersection_list(
managedby_host, res_find.get("managedby_host"))
if managedby_host_del is not None:
for _managedby_host in managedby_host_del:
commands.append([name, "host_remove_managedby",
{
"host":
@@ -1389,8 +1493,10 @@ def main():
# the removal of non-existing entries.
# Remove principals
if principal is not None:
for _principal in principal:
principal_del = gen_intersection_list(
principal, res_find.get("krbprincipalname"))
if principal_del is not None:
for _principal in principal_del:
commands.append([name, "host_remove_principal",
{
"krbprincipalname":
@@ -1398,60 +1504,86 @@ def main():
}])
# Disallow create keytab
if allow_create_keytab_user is not None or \
allow_create_keytab_group is not None or \
allow_create_keytab_host is not None or \
allow_create_keytab_hostgroup is not None:
allow_create_keytab_user_del = gen_intersection_list(
allow_create_keytab_user,
res_find.get("ipaallowedtoperform_write_keys_user"))
allow_create_keytab_group_del = gen_intersection_list(
allow_create_keytab_group,
res_find.get("ipaallowedtoperform_write_keys_group"))
allow_create_keytab_host_del = gen_intersection_list(
allow_create_keytab_host,
res_find.get("ipaallowedtoperform_write_keys_host"))
allow_create_keytab_hostgroup_del = gen_intersection_list(
allow_create_keytab_hostgroup,
res_find.get(
"ipaallowedtoperform_write_keys_hostgroup"))
if len(allow_create_keytab_user_del) > 0 or \
len(allow_create_keytab_group_del) > 0 or \
len(allow_create_keytab_host_del) > 0 or \
len(allow_create_keytab_hostgroup_del) > 0:
commands.append(
[name, "host_disallow_create_keytab",
{
"user": allow_create_keytab_user,
"group": allow_create_keytab_group,
"host": allow_create_keytab_host,
"hostgroup": allow_create_keytab_hostgroup,
"user": allow_create_keytab_user_del,
"group": allow_create_keytab_group_del,
"host": allow_create_keytab_host_del,
"hostgroup":
allow_create_keytab_hostgroup_del,
}])
# Disallow retrieve keytab
if allow_retrieve_keytab_user is not None or \
allow_retrieve_keytab_group is not None or \
allow_retrieve_keytab_host is not None or \
allow_retrieve_keytab_hostgroup is not None:
allow_retrieve_keytab_user_del = gen_intersection_list(
allow_retrieve_keytab_user,
res_find.get("ipaallowedtoperform_read_keys_user"))
allow_retrieve_keytab_group_del = gen_intersection_list(
allow_retrieve_keytab_group,
res_find.get("ipaallowedtoperform_read_keys_group"))
allow_retrieve_keytab_host_del = gen_intersection_list(
allow_retrieve_keytab_host,
res_find.get("ipaallowedtoperform_read_keys_host"))
allow_retrieve_keytab_hostgroup_del = \
gen_intersection_list(
allow_retrieve_keytab_hostgroup,
res_find.get(
"ipaallowedtoperform_read_keys_hostgroup"))
if len(allow_retrieve_keytab_user_del) > 0 or \
len(allow_retrieve_keytab_group_del) > 0 or \
len(allow_retrieve_keytab_host_del) > 0 or \
len(allow_retrieve_keytab_hostgroup_del) > 0:
commands.append(
[name, "host_disallow_retrieve_keytab",
{
"user": allow_retrieve_keytab_user,
"group": allow_retrieve_keytab_group,
"host": allow_retrieve_keytab_host,
"hostgroup": allow_retrieve_keytab_hostgroup,
"user": allow_retrieve_keytab_user_del,
"group": allow_retrieve_keytab_group_del,
"host": allow_retrieve_keytab_host_del,
"hostgroup":
allow_retrieve_keytab_hostgroup_del,
}])
dnsrecord_args = gen_dnsrecord_args(ansible_module,
ip_address, reverse)
if res_find_dnsrecord is not None:
dnsrecord_args = gen_dnsrecord_args(
ansible_module, ip_address, reverse)
# Remove arecord and aaaarecord from dnsrecord_args
# if the record does not exits in res_find_dnsrecord
# to prevent "DNS resource record not found" error
if "arecord" in dnsrecord_args \
and dnsrecord_args["arecord"] is not None \
and len(dnsrecord_args["arecord"]) > 0 \
and (res_find_dnsrecord is None
or "arecord" not in res_find_dnsrecord):
del dnsrecord_args["arecord"]
if "aaaarecord" in dnsrecord_args \
and dnsrecord_args["aaaarecord"] is not None \
and len(dnsrecord_args["aaaarecord"]) > 0 \
and (res_find_dnsrecord is None
or "aaaarecord" not in res_find_dnsrecord):
del dnsrecord_args["aaaarecord"]
# Only keep a and aaaa recrords that are part
# of res_find_dnsrecord.
for _type in ["arecord", "aaaarecord"]:
if _type in dnsrecord_args:
recs = gen_intersection_list(
dnsrecord_args[_type],
res_find_dnsrecord.get(_type))
if len(recs) > 0:
dnsrecord_args[_type] = recs
else:
del dnsrecord_args[_type]
if "arecord" in dnsrecord_args or \
"aaaarecord" in dnsrecord_args:
domain_name = name[name.find(".") + 1:]
host_name = name[:name.find(".")]
dnsrecord_args["idnsname"] = host_name
if "arecord" in dnsrecord_args or \
"aaaarecord" in dnsrecord_args:
domain_name = name[name.find(".") + 1:]
host_name = name[:name.find(".")]
dnsrecord_args["idnsname"] = host_name
commands.append([domain_name, "dnsrecord_del",
dnsrecord_args])
commands.append([domain_name, "dnsrecord_del",
dnsrecord_args])
elif state == "disabled":
if res_find is not None:
@@ -1467,8 +1599,8 @@ def main():
# Execute commands
changed = ansible_module.execute_ipa_commands(
commands, result_handler, exception_handler,
exit_args=exit_args, one_name=len(names) == 1)
commands, result_handler, batch=True, keeponly=["randompassword"],
exit_args=exit_args, single_host=hosts is None)
# Done

View File

@@ -181,16 +181,6 @@ def gen_args(description, nomembers, rename):
return _args
def gen_member_args(host, hostgroup):
_args = {}
if host is not None:
_args["member_host"] = host
if hostgroup is not None:
_args["member_hostgroup"] = hostgroup
return _args
def main():
ansible_module = IPAAnsibleModule(
argument_spec=dict(
@@ -224,16 +214,20 @@ def main():
# Get parameters
# general
names = ansible_module.params_get("name")
names = ansible_module.params_get_lowercase("name")
# present
description = ansible_module.params_get("description")
nomembers = ansible_module.params_get("nomembers")
host = ansible_module.params_get("host")
hostgroup = ansible_module.params_get("hostgroup")
membermanager_user = ansible_module.params_get("membermanager_user")
membermanager_group = ansible_module.params_get("membermanager_group")
rename = ansible_module.params_get("rename")
hostgroup = ansible_module.params_get_lowercase("hostgroup")
membermanager_user = ansible_module.params_get_lowercase(
"membermanager_user"
)
membermanager_group = ansible_module.params_get_lowercase(
"membermanager_group"
)
rename = ansible_module.params_get_lowercase("rename")
action = ansible_module.params_get("action")
# state
state = ansible_module.params_get("state")
@@ -306,6 +300,12 @@ def main():
commands = []
for name in names:
# clean add/del lists
host_add, host_del = [], []
hostgroup_add, hostgroup_del = [], []
membermanager_user_add, membermanager_user_del = [], []
membermanager_group_add, membermanager_group_del = [], []
# Make sure hostgroup exists
res_find = find_hostgroup(ansible_module, name)
@@ -328,63 +328,26 @@ def main():
# Set res_find to empty dict for next step
res_find = {}
member_args = gen_member_args(host, hostgroup)
if not compare_args_ipa(ansible_module, member_args,
res_find):
# Generate addition and removal lists
host_add, host_del = gen_add_del_lists(
host, res_find.get("member_host"))
# Generate addition and removal lists
host_add, host_del = gen_add_del_lists(
host, res_find.get("member_host")
)
hostgroup_add, hostgroup_del = gen_add_del_lists(
hostgroup, res_find.get("member_hostgroup"))
# Add members
if len(host_add) > 0 or len(hostgroup_add) > 0:
commands.append([name, "hostgroup_add_member",
{
"host": host_add,
"hostgroup": hostgroup_add,
}])
# Remove members
if len(host_del) > 0 or len(hostgroup_del) > 0:
commands.append([name, "hostgroup_remove_member",
{
"host": host_del,
"hostgroup": hostgroup_del,
}])
membermanager_user_add, membermanager_user_del = \
gen_add_del_lists(
membermanager_user,
res_find.get("membermanager_user")
)
membermanager_group_add, membermanager_group_del = \
gen_add_del_lists(
membermanager_group,
res_find.get("membermanager_group")
)
hostgroup_add, hostgroup_del = gen_add_del_lists(
hostgroup, res_find.get("member_hostgroup")
)
if has_add_membermanager:
# Add membermanager users and groups
if len(membermanager_user_add) > 0 or \
len(membermanager_group_add) > 0:
commands.append(
[name, "hostgroup_add_member_manager",
{
"user": membermanager_user_add,
"group": membermanager_group_add,
}]
membermanager_user_add, membermanager_user_del = \
gen_add_del_lists(
membermanager_user,
res_find.get("membermanager_user")
)
# Remove member manager
if len(membermanager_user_del) > 0 or \
len(membermanager_group_del) > 0:
commands.append(
[name, "hostgroup_remove_member_manager",
{
"user": membermanager_user_del,
"group": membermanager_group_del,
}]
membermanager_group_add, membermanager_group_del = \
gen_add_del_lists(
membermanager_group,
res_find.get("membermanager_group")
)
elif action == "member":
@@ -394,45 +357,25 @@ def main():
# Reduce add lists for member_host and member_hostgroup,
# to new entries only that are not in res_find.
if host is not None and "member_host" in res_find:
host = gen_add_list(host, res_find["member_host"])
if hostgroup is not None \
and "member_hostgroup" in res_find:
hostgroup = gen_add_list(
hostgroup, res_find["member_hostgroup"])
# Ensure members are present
commands.append([name, "hostgroup_add_member",
{
"host": host,
"hostgroup": hostgroup,
}])
host_add = gen_add_list(
host, res_find.get("member_host")
)
hostgroup_add = gen_add_list(
hostgroup, res_find.get("member_hostgroup")
)
if has_add_membermanager:
# Reduce add list for membermanager_user and
# membermanager_group to new entries only that are
# not in res_find.
if membermanager_user is not None \
and "membermanager_user" in res_find:
membermanager_user = gen_add_list(
membermanager_user,
res_find["membermanager_user"])
if membermanager_group is not None \
and "membermanager_group" in res_find:
membermanager_group = gen_add_list(
membermanager_group,
res_find["membermanager_group"])
# Add membermanager users and groups
if membermanager_user is not None or \
membermanager_group is not None:
commands.append(
[name, "hostgroup_add_member_manager",
{
"user": membermanager_user,
"group": membermanager_group,
}]
)
membermanager_user_add = gen_add_list(
membermanager_user,
res_find.get("membermanager_user")
)
membermanager_group_add = gen_add_list(
membermanager_group,
res_find.get("membermanager_group")
)
elif state == "renamed":
if res_find is not None:
@@ -463,46 +406,72 @@ def main():
# Reduce del lists of member_host and member_hostgroup,
# to the entries only that are in res_find.
if host is not None:
host = gen_intersection_list(
host, res_find.get("member_host"))
host_del = gen_intersection_list(
host, res_find.get("member_host")
)
if hostgroup is not None:
hostgroup = gen_intersection_list(
hostgroup, res_find.get("member_hostgroup"))
# Ensure members are absent
commands.append([name, "hostgroup_remove_member",
{
"host": host,
"hostgroup": hostgroup,
}])
hostgroup_del = gen_intersection_list(
hostgroup, res_find.get("member_hostgroup")
)
if has_add_membermanager:
# Reduce del lists of membermanager_user and
# membermanager_group to the entries only that are
# in res_find.
if membermanager_user is not None:
membermanager_user = gen_intersection_list(
membermanager_user,
res_find.get("membermanager_user"))
if membermanager_group is not None:
membermanager_group = gen_intersection_list(
membermanager_group,
res_find.get("membermanager_group"))
# Remove membermanager users and groups
if membermanager_user is not None or \
membermanager_group is not None:
commands.append(
[name, "hostgroup_remove_member_manager",
{
"user": membermanager_user,
"group": membermanager_group,
}]
)
# Get lists of membermanager users that exist
# in IPA and should be removed.
membermanager_user_del = gen_intersection_list(
membermanager_user,
res_find.get("membermanager_user")
)
membermanager_group_del = gen_intersection_list(
membermanager_group,
res_find.get("membermanager_group")
)
else:
ansible_module.fail_json(msg="Unkown state '%s'" % state)
# Manage members
# Add members
if host_add or hostgroup_add:
commands.append([
name, "hostgroup_add_member",
{
"host": host_add,
"hostgroup": hostgroup_add,
}
])
# Remove members
if host_del or hostgroup_del:
commands.append([
name, "hostgroup_remove_member",
{
"host": host_del,
"hostgroup": hostgroup_del,
}
])
# Manage membermanager users and groups
if has_add_membermanager:
# Add membermanager users and groups
if membermanager_user_add or membermanager_group_add:
commands.append([
name, "hostgroup_add_member_manager",
{
"user": membermanager_user_add,
"group": membermanager_group_add,
}
])
# Remove membermanager users and groups
if membermanager_user_del or membermanager_group_del:
commands.append([
name, "hostgroup_remove_member_manager",
{
"user": membermanager_user_del,
"group": membermanager_group_del,
}
])
# Execute commands
changed = ansible_module.execute_ipa_commands(

View File

@@ -0,0 +1,341 @@
# -*- coding: utf-8 -*-
# Authors:
# Thomas Woerner <twoerner@redhat.com>
#
# Copyright (C) 2023 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, exither 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"],
}
# No rename support: 'ID overrides cannot be renamed'
# ipaserver/plugins/idviews.py:baseidoverride_mod:pre_callback
DOCUMENTATION = """
---
module: ipaidoverridegroup
short_description: Manage FreeIPA idoverridegroup
description: Manage FreeIPA idoverridegroups
extends_documentation_fragment:
- ipamodule_base_docs
options:
idview:
description: The idoverridegroup idview string.
type: str
required: true
aliases: ["idviewcn"]
anchor:
description: The list of anchors to override
type: list
elements: str
required: true
aliases: ["ipaanchoruuid"]
description:
description: Description
type: str
required: False
aliases: ["desc"]
name:
description: Group name
type: str
required: False
aliases: ["group_name", "cn"]
gid:
description: Group ID Number (int or "")
type: str
required: False
aliases: ["gidnumber"]
fallback_to_ldap:
description: |
Allow falling back to AD DC LDAP when resolving AD trusted objects.
For two-way trusts only.
required: False
type: bool
delete_continue:
description: |
Continuous mode. Don't stop on errors.
Valid only if `state` is `absent`.
required: false
type: bool
aliases: ["continue"]
state:
description: The state to ensure.
choices: ["present", "absent"]
default: present
type: str
author:
- Thomas Woerner (@t-woerner)
"""
EXAMPLES = """
# Ensure test group test_group is present in idview test_idview
- ipaidoverridegroup:
ipaadmin_password: SomeADMINpassword
idview: test_idview
anchor: test_group
# Ensure test group test_group is present in idview test_idview with
# description
- ipaidoverridegroup:
ipaadmin_password: SomeADMINpassword
idview: test_idview
anchor: test_group
description: "test_group description"
# Ensure test group test_group is present in idview test_idview without
# description
- ipaidoverridegroup:
ipaadmin_password: SomeADMINpassword
idview: test_idview
anchor: test_group
description: ""
# Ensure test group test_group is present in idview test_idview with internal
# name test_123_group
- ipaidoverridegroup:
ipaadmin_password: SomeADMINpassword
idview: test_idview
anchor: test_group
name: test_123_group
# Ensure test group test_group is present in idview test_idview without
# internal name
- ipaidoverridegroup:
ipaadmin_password: SomeADMINpassword
idview: test_idview
anchor: test_group
name: ""
# Ensure test group test_group is present in idview test_idview with gid 20001
- ipaidoverridegroup:
ipaadmin_password: SomeADMINpassword
idview: test_idview
anchor: test_group
gid: 20001
# Ensure test group test_group is present in idview test_idview without gid
- ipaidoverridegroup:
ipaadmin_password: SomeADMINpassword
idview: test_idview
anchor: test_group
gid: ""
# Ensure test group test_group is absent in idview test_idview
- ipaidoverridegroup:
ipaadmin_password: SomeADMINpassword
idview: test_idview
anchor: test_group
continue: true
state: absent
"""
RETURN = """
"""
from ansible.module_utils.ansible_freeipa_module import \
IPAAnsibleModule, compare_args_ipa
from ansible.module_utils import six
if six.PY3:
unicode = str
def find_idoverridegroup(module, idview, anchor):
"""Find if a idoverridegroup with the given name already exist."""
try:
_result = module.ipa_command("idoverridegroup_show", idview,
{"ipaanchoruuid": anchor,
"all": True})
except Exception: # pylint: disable=broad-except
# An exception is raised if idoverridegroup anchor is not found.
return None
return _result["result"]
def gen_args(anchor, description, name, gid):
# fallback_to_ldap is only a runtime tuning parameter
_args = {}
if anchor is not None:
_args["ipaanchoruuid"] = anchor
if description is not None:
_args["description"] = description
if name is not None:
_args["cn"] = name
if gid is not None:
_args["gidnumber"] = gid
return _args
def gen_args_runtime(fallback_to_ldap):
_args = {}
if fallback_to_ldap is not None:
_args["fallback_to_ldap"] = fallback_to_ldap
return _args
def merge_dicts(dict1, dict2):
ret = dict1.copy()
ret.update(dict2)
return ret
def main():
ansible_module = IPAAnsibleModule(
argument_spec=dict(
# general
idview=dict(type="str", required=True, aliases=["idviewcn"]),
anchor=dict(type="list", elements="str", required=True,
aliases=["ipaanchoruuid"]),
# present
description=dict(type="str", required=False, aliases=["desc"]),
name=dict(type="str", required=False,
aliases=["group_name", "cn"]),
gid=dict(type="str", required=False, aliases=["gidnumber"]),
# runtime flags
fallback_to_ldap=dict(type="bool", required=False),
# absent
delete_continue=dict(type="bool", required=False,
aliases=['continue'], default=None),
# No rename support: 'ID overrides cannot be renamed'
# ipaserver/plugins/idviews.py:baseidoverride_mod:pre_callback
# state
state=dict(type="str", default="present",
choices=["present", "absent"]),
),
supports_check_mode=True,
)
ansible_module._ansible_debug = True
# Get parameters
# general
idview = ansible_module.params_get("idview")
anchors = ansible_module.params_get("anchor")
# present
description = ansible_module.params_get("description")
name = ansible_module.params_get("name")
gid = ansible_module.params_get_with_type_cast("gid", int)
# runtime flags
fallback_to_ldap = ansible_module.params_get("fallback_to_ldap")
# absent
delete_continue = ansible_module.params_get("delete_continue")
# state
state = ansible_module.params_get("state")
# Check parameters
invalid = []
if state == "present":
if len(anchors) != 1:
ansible_module.fail_json(
msg="Only one idoverridegroup can be added at a time.")
invalid = ["delete_continue"]
if state == "absent":
if len(anchors) < 1:
ansible_module.fail_json(msg="No name given.")
invalid = ["description", "name", "gid"]
ansible_module.params_fail_used_invalid(invalid, state)
# Init
changed = False
exit_args = {}
# Connect to IPA API
with ansible_module.ipa_connect():
runtime_args = gen_args_runtime(fallback_to_ldap)
commands = []
for anchor in anchors:
# Make sure idoverridegroup exists
res_find = find_idoverridegroup(ansible_module, idview, anchor)
# Create command
if state == "present":
# Generate args
args = gen_args(anchor, description, name, gid)
# fallback_to_ldap is only a runtime tuning parameter
all_args = merge_dicts(args, runtime_args)
# Found the idoverridegroup
if res_find is not None:
# For idempotency: Remove empty sshpubkey list if
# there are no sshpubkey in the found entry.
if "ipasshpubkey" in args and \
len(args["ipasshpubkey"]) < 1 and \
"ipasshpubkey" not in res_find:
del args["ipasshpubkey"]
# For all settings is args, check if there are
# different settings in the find result.
# If yes: modify
if not compare_args_ipa(ansible_module, args,
res_find):
commands.append([idview, "idoverridegroup_mod",
all_args])
else:
commands.append([idview, "idoverridegroup_add",
all_args])
elif state == "absent":
if res_find is not None:
commands.append(
[idview, "idoverridegroup_del",
merge_dicts(
{
"ipaanchoruuid": anchor,
"continue": delete_continue or False
},
runtime_args
)]
)
else:
ansible_module.fail_json(msg="Unkown state '%s'" % state)
# Execute commands
changed = ansible_module.execute_ipa_commands(commands)
# Done
ansible_module.exit_json(changed=changed, **exit_args)
if __name__ == "__main__":
main()

View File

@@ -0,0 +1,617 @@
# -*- coding: utf-8 -*-
# Authors:
# Thomas Woerner <twoerner@redhat.com>
#
# Copyright (C) 2023 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"],
}
# No rename support: 'ID overrides cannot be renamed'
# ipaserver/plugins/idviews.py:baseidoverride_mod:pre_callback
DOCUMENTATION = """
---
module: ipaidoverrideuser
short_description: Manage FreeIPA idoverrideuser
description: Manage FreeIPA idoverrideuser and idoverrideuser members
extends_documentation_fragment:
- ipamodule_base_docs
options:
idview:
description: The idoverrideuser idview string.
type: str
required: true
aliases: ["idviewcn"]
anchor:
description: The list of anchors to override
type: list
elements: str
required: true
aliases: ["ipaanchoruuid"]
description:
description: Description
type: str
required: False
aliases: ["desc"]
name:
description: The user (internally uid)
type: str
required: False
aliases: ["login"]
uid:
description: User ID Number (int or "")
type: str
required: False
aliases: ["uidnumber"]
gecos:
description: GECOS
required: False
type: str
gidnumber:
description: Group ID Number (int or "")
required: False
type: str
homedir:
description: Home directory
type: str
required: False
aliases: ["homedirectory"]
shell:
description: Login shell
type: str
required: False
aliases: ["loginshell"]
sshpubkey:
description: List of SSH public keys
type: list
elements: str
required: False
aliases: ["ipasshpubkey"]
certificate:
description: List of Base-64 encoded user certificates
type: list
elements: str
required: False
aliases: ["usercertificate"]
fallback_to_ldap:
description: |
Allow falling back to AD DC LDAP when resolving AD trusted objects.
For two-way trusts only.
required: False
type: bool
delete_continue:
description: |
Continuous mode. Don't stop on errors.
Valid only if `state` is `absent`.
required: false
type: bool
aliases: ["continue"]
nomembers:
description: |
Suppress processing of membership attributes.
Valid only if `state` is `absent`.
type: bool
required: False
aliases: ["no_members"]
action:
description: Work on idoverrideuser or member level.
choices: ["idoverrideuser", "member"]
default: idoverrideuser
type: str
state:
description: The state to ensure.
choices: ["present", "absent"]
default: present
type: str
author:
- Thomas Woerner (@t-woerner)
"""
EXAMPLES = """
# Ensure test user test_user is present in idview test_idview
- ipaidoverrideuser:
ipaadmin_password: SomeADMINpassword
idview: test_idview
anchor: test_user
# Ensure test user test_user is present in idview test_idview with description
- ipaidoverrideuser:
ipaadmin_password: SomeADMINpassword
idview: test_idview
anchor: test_user
description: "test_user description"
# Ensure test user test_user is present in idview test_idview without
# description
- ipaidoverrideuser:
ipaadmin_password: SomeADMINpassword
idview: test_idview
anchor: test_user
description: ""
# Ensure test user test_user is present in idview test_idview with internal
# name test_123_user
- ipaidoverrideuser:
ipaadmin_password: SomeADMINpassword
idview: test_idview
anchor: test_user
name: test_123_user
# Ensure test user test_user is present in idview test_idview without internal
# name
- ipaidoverrideuser:
ipaadmin_password: SomeADMINpassword
idview: test_idview
anchor: test_user
name: ""
# Ensure test user test_user is present in idview test_idview with uid 20001
- ipaidoverrideuser:
ipaadmin_password: SomeADMINpassword
idview: test_idview
anchor: test_user
uid: 20001
# Ensure test user test_user is present in idview test_idview without uid
- ipaidoverrideuser:
ipaadmin_password: SomeADMINpassword
idview: test_idview
anchor: test_user
uid: ""
# Ensure test user test_user is present in idview test_idview with gecos
# "Gecos Test"
- ipaidoverrideuser:
ipaadmin_password: SomeADMINpassword
idview: test_idview
anchor: test_user
gecos: Gecos Test
# Ensure test user test_user is present in idview test_idview without gecos
- ipaidoverrideuser:
ipaadmin_password: SomeADMINpassword
idview: test_idview
anchor: test_user
gecos: ""
# Ensure test user test_user is present in idview test_idview with gidnumber
# 20001
- ipaidoverrideuser:
ipaadmin_password: SomeADMINpassword
idview: test_idview
anchor: test_user
gidnumber: 20001
# Ensure test user test_user is present in idview test_idview without
# gidnumber
- ipaidoverrideuser:
ipaadmin_password: SomeADMINpassword
idview: test_idview
anchor: test_user
gidnumber: ""
# Ensure test user test_user is present in idview test_idview with homedir
# /Users
- ipaidoverrideuser:
ipaadmin_password: SomeADMINpassword
idview: test_idview
anchor: test_user
homedir: /Users
# Ensure test user test_user is present in idview test_idview without homedir
- ipaidoverrideuser:
ipaadmin_password: SomeADMINpassword
idview: test_idview
anchor: test_user
homedir: ""
# Ensure test user test_user is present in idview test_idview with shell
# /bin/someshell
- ipaidoverrideuser:
ipaadmin_password: SomeADMINpassword
idview: test_idview
anchor: test_user
shell: /bin/someshell
# Ensure test user test_user is present in idview test_idview without shell
- ipaidoverrideuser:
ipaadmin_password: SomeADMINpassword
idview: test_idview
anchor: test_user
shell: ""
# Ensure test user test_user is present in idview test_idview with sshpubkey
- ipaidoverrideuser:
ipaadmin_password: SomeADMINpassword
idview: test_idview
anchor: test_user
sshpubkey:
- ssh-rsa AAAAB3NzaC1yc2EAAADAQABAAABgQCqmVDpEX5gnSjKuv97Ay ...
# Ensure test user test_user is present in idview test_idview without
# sshpubkey
- ipaidoverrideuser:
ipaadmin_password: SomeADMINpassword
idview: test_idview
anchor: test_user
sshpubkey: []
# Ensure test user test_user is present in idview test_idview with 1
# certificate
- ipaidoverrideuser:
ipaadmin_password: SomeADMINpassword
idview: test_idview
anchor: test_user
certificate:
- "{{ lookup('file', 'cert1.b64', rstrip=False) }}"
# Ensure test user test_user is present in idview test_idview with 3
# certificate members
- ipaidoverrideuser:
ipaadmin_password: SomeADMINpassword
idview: test_idview
anchor: test_user
certificate:
- "{{ lookup('file', 'cert1.b64', rstrip=False) }}"
- "{{ lookup('file', 'cert2.b64', rstrip=False) }}"
- "{{ lookup('file', 'cert3.b64', rstrip=False) }}"
action: member
# Ensure test user test_user is present in idview test_idview without
# 2 certificate members
- ipaidoverrideuser:
ipaadmin_password: SomeADMINpassword
idview: test_idview
anchor: test_user
certificate:
- "{{ lookup('file', 'cert2.b64', rstrip=False) }}"
- "{{ lookup('file', 'cert3.b64', rstrip=False) }}"
action: member
state: absent
# Ensure test user test_user is present in idview test_idview without
# certificates
- ipaidoverrideuser:
ipaadmin_password: SomeADMINpassword
idview: test_idview
anchor: test_user
certificate: []
# Ensure test user test_user is absent in idview test_idview
- ipaidoverrideuser:
ipaadmin_password: SomeADMINpassword
idview: test_idview
anchor: test_user
continue: true
state: absent
"""
RETURN = """
"""
from ansible.module_utils.ansible_freeipa_module import \
IPAAnsibleModule, compare_args_ipa, gen_add_del_lists, gen_add_list, \
gen_intersection_list, encode_certificate, convert_input_certificates
from ansible.module_utils import six
if six.PY3:
unicode = str
def find_idoverrideuser(module, idview, anchor):
"""Find if a idoverrideuser with the given name already exist."""
try:
_result = module.ipa_command("idoverrideuser_show", idview,
{"ipaanchoruuid": anchor,
"all": True})
except Exception: # pylint: disable=broad-except
# An exception is raised if idoverrideuser anchor is not found.
return None
_res = _result["result"]
certs = _res.get("usercertificate")
if certs is not None:
_res["usercertificate"] = [encode_certificate(cert) for cert in certs]
return _res
def gen_args(anchor, description, name, uid, gecos, gidnumber, homedir, shell,
sshpubkey):
# fallback_to_ldap and nomembers are only runtime tuning parameters
_args = {}
if anchor is not None:
_args["ipaanchoruuid"] = anchor
if description is not None:
_args["description"] = description
if name is not None:
_args["uid"] = name
if uid is not None:
_args["uidnumber"] = uid
if gecos is not None:
_args["gecos"] = gecos
if gidnumber is not None:
_args["gidnumber"] = gidnumber
if homedir is not None:
_args["homedirectory"] = homedir
if shell is not None:
_args["loginshell"] = shell
if sshpubkey is not None:
_args["ipasshpubkey"] = sshpubkey
return _args
def gen_args_runtime(fallback_to_ldap, nomembers):
_args = {}
if fallback_to_ldap is not None:
_args["fallback_to_ldap"] = fallback_to_ldap
if nomembers is not None:
_args["no_members"] = nomembers
return _args
def gen_member_args(certificate):
_args = {}
if certificate is not None:
_args["usercertificate"] = certificate
return _args
def merge_dicts(dict1, dict2):
ret = dict1.copy()
ret.update(dict2)
return ret
def main():
ansible_module = IPAAnsibleModule(
argument_spec=dict(
# general
idview=dict(type="str", required=True, aliases=["idviewcn"]),
anchor=dict(type="list", elements="str", required=True,
aliases=["ipaanchoruuid"]),
# present
description=dict(type="str", required=False, aliases=["desc"]),
name=dict(type="str", required=False, aliases=["login"]),
uid=dict(type="str", required=False, aliases=["uidnumber"]),
gecos=dict(type="str", required=False),
gidnumber=dict(type="str", required=False),
homedir=dict(type="str", required=False,
aliases=["homedirectory"]),
shell=dict(type="str", required=False, aliases=["loginshell"]),
sshpubkey=dict(type="list", elements="str", required=False,
aliases=["ipasshpubkey"]),
certificate=dict(type="list", elements="str", required=False,
aliases=["usercertificate"]),
fallback_to_ldap=dict(type="bool", required=False),
nomembers=dict(type="bool", required=False,
aliases=["no_members"]),
# absent
delete_continue=dict(type="bool", required=False,
aliases=['continue'], default=None),
# No rename support: 'ID overrides cannot be renamed'
# ipaserver/plugins/idviews.py:baseidoverride_mod:pre_callback
# action
action=dict(type="str", default="idoverrideuser",
choices=["member", "idoverrideuser"]),
# state
state=dict(type="str", default="present",
choices=["present", "absent"]),
),
supports_check_mode=True,
)
ansible_module._ansible_debug = True
# Get parameters
# general
idview = ansible_module.params_get("idview")
anchors = ansible_module.params_get("anchor")
# present
description = ansible_module.params_get("description")
name = ansible_module.params_get("name")
uid = ansible_module.params_get_with_type_cast("uid", int)
gecos = ansible_module.params_get("gecos")
gidnumber = ansible_module.params_get_with_type_cast("gidnumber", int)
homedir = ansible_module.params_get("homedir")
shell = ansible_module.params_get("shell")
sshpubkey = ansible_module.params_get("sshpubkey")
certificate = ansible_module.params_get("certificate")
fallback_to_ldap = ansible_module.params_get("fallback_to_ldap")
nomembers = ansible_module.params_get("nomembers")
action = ansible_module.params_get("action")
# absent
delete_continue = ansible_module.params_get("delete_continue")
# state
state = ansible_module.params_get("state")
# Check parameters
invalid = []
if state == "present":
if len(anchors) != 1:
ansible_module.fail_json(
msg="Only one idoverrideuser can be added at a time.")
invalid = ["delete_continue"]
if action == "member":
invalid += ["description", "name", "uid", "gecos", "gidnumber",
"homedir", "shell", "sshpubkey"]
if state == "absent":
if len(anchors) < 1:
ansible_module.fail_json(msg="No name given.")
invalid = ["description", "name", "uid", "gecos", "gidnumber",
"homedir", "shell", "sshpubkey", "nomembers"]
if action == "idoverrideuser":
invalid += ["certificate"]
ansible_module.params_fail_used_invalid(invalid, state, action)
certificate = convert_input_certificates(ansible_module, certificate,
state)
# Init
changed = False
exit_args = {}
# Connect to IPA API
with ansible_module.ipa_connect():
runtime_args = gen_args_runtime(fallback_to_ldap, nomembers)
commands = []
for anchor in anchors:
# Make sure idoverrideuser exists
res_find = find_idoverrideuser(ansible_module, idview, anchor)
# add/del lists
certificate_add, certificate_del = [], []
# Create command
if state == "present":
# Generate args
args = gen_args(anchor, description, name, uid, gecos,
gidnumber, homedir, shell, sshpubkey)
# fallback_to_ldap and nomembers are only runtime tuning
# parameters
all_args = merge_dicts(args, runtime_args)
if action == "idoverrideuser":
# Found the idoverrideuser
if res_find is not None:
# For idempotency: Remove empty sshpubkey list if
# there are no sshpubkey in the found entry.
if "ipasshpubkey" in args and \
len(args["ipasshpubkey"]) < 1 and \
"ipasshpubkey" not in res_find:
del args["ipasshpubkey"]
# For all settings is args, check if there are
# different settings in the find result.
# If yes: modify
if not compare_args_ipa(ansible_module, args,
res_find):
commands.append([idview, "idoverrideuser_mod",
all_args])
else:
commands.append([idview, "idoverrideuser_add",
all_args])
res_find = {}
member_args = gen_member_args(certificate)
if not compare_args_ipa(ansible_module, member_args,
res_find):
# Generate addition and removal lists
certificate_add, certificate_del = gen_add_del_lists(
certificate, res_find.get("usercertificate"))
elif action == "member":
if res_find is None:
ansible_module.fail_json(
msg="No idoverrideuser '%s' in idview '%s'" %
(anchor, idview))
# Reduce add lists for certificate
# to new entries only that are not in res_find.
if certificate is not None:
certificate_add = gen_add_list(
certificate, res_find.get("usercertificate"))
elif state == "absent":
if action == "idoverrideuser":
if res_find is not None:
commands.append(
[idview, "idoverrideuser_del",
merge_dicts(
{
"ipaanchoruuid": anchor,
"continue": delete_continue or False
},
runtime_args
)]
)
elif action == "member":
if res_find is None:
ansible_module.fail_json(
msg="No idoverrideuser '%s' in idview '%s'" %
(anchor, idview))
# Reduce del lists of member_host and member_hostgroup,
# to the entries only that are in res_find.
if certificate is not None:
certificate_del = gen_intersection_list(
certificate, res_find.get("usercertificate"))
else:
ansible_module.fail_json(msg="Unkown state '%s'" % state)
# Member management
# Add members
if certificate_add:
commands.append([idview, "idoverrideuser_add_cert",
merge_dicts(
{
"ipaanchoruuid": anchor,
"usercertificate": certificate_add
},
runtime_args
)])
# Remove members
if certificate_del:
commands.append([idview, "idoverrideuser_remove_cert",
merge_dicts(
{
"ipaanchoruuid": anchor,
"usercertificate": certificate_del
},
runtime_args
)])
# Execute commands
changed = ansible_module.execute_ipa_commands(commands)
# Done
ansible_module.exit_json(changed=changed, **exit_args)
if __name__ == "__main__":
main()

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