mirror of
https://github.com/freeipa/ansible-freeipa.git
synced 2026-03-31 15:53:06 +00:00
Compare commits
41 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
cf779e43bb | ||
|
|
1a48a0fb63 | ||
|
|
ed3a0d5a1b | ||
|
|
d58b492f1d | ||
|
|
88d4a36e17 | ||
|
|
6fa8223662 | ||
|
|
c9e8656494 | ||
|
|
a791c6a0ca | ||
|
|
9cbccdade9 | ||
|
|
42c07d6336 | ||
|
|
a728a8d43e | ||
|
|
bd3266e9f1 | ||
|
|
48063d2b3a | ||
|
|
5d08214516 | ||
|
|
ef0b7e80f0 | ||
|
|
a33fcf45f8 | ||
|
|
c4b273c896 | ||
|
|
62d34d0a22 | ||
|
|
3ed0c229c4 | ||
|
|
c089c010e6 | ||
|
|
cfbdd83a64 | ||
|
|
fef1bdcf8e | ||
|
|
411d363d91 | ||
|
|
1555132d85 | ||
|
|
57ad57dda3 | ||
|
|
dab64c7cf6 | ||
|
|
b7145bc2cc | ||
|
|
c9f1da5d6b | ||
|
|
f4070f6a30 | ||
|
|
ad9a03ece6 | ||
|
|
1bfe6888a4 | ||
|
|
51ddaa6491 | ||
|
|
f56861cc15 | ||
|
|
c4de680497 | ||
|
|
7b2701b985 | ||
|
|
694c717829 | ||
|
|
083396e133 | ||
|
|
9a8a1db38f | ||
|
|
8f9c344bc1 | ||
|
|
067b683b81 | ||
|
|
51f64e4393 |
2
.github/workflows/ansible-test.yml
vendored
2
.github/workflows/ansible-test.yml
vendored
@@ -11,7 +11,5 @@ jobs:
|
||||
- uses: actions/checkout@v3.1.0
|
||||
with:
|
||||
fetch-depth: 0
|
||||
- name: Install virtualenv using pip
|
||||
run: pip install virtualenv
|
||||
- name: Run ansible-test
|
||||
run: bash tests/sanity/sanity.sh
|
||||
|
||||
36
.github/workflows/docs.yml
vendored
36
.github/workflows/docs.yml
vendored
@@ -5,23 +5,6 @@ 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:
|
||||
@@ -38,7 +21,7 @@ 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:
|
||||
@@ -55,6 +38,23 @@ 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@v3.1.0
|
||||
with:
|
||||
fetch-depth: 0
|
||||
- uses: actions/setup-python@v4.3.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
|
||||
|
||||
2
.github/workflows/lint.yml
vendored
2
.github/workflows/lint.yml
vendored
@@ -16,7 +16,7 @@ jobs:
|
||||
python-version: "3.x"
|
||||
- name: Run ansible-lint
|
||||
run: |
|
||||
pip install "ansible-core >=2.14,<2.15" ansible-lint
|
||||
pip install "ansible-core >=2.15,<2.16" ansible-lint
|
||||
utils/build-galaxy-release.sh -ki
|
||||
cd .galaxy-build
|
||||
ansible-lint
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
|
||||
|
||||
@@ -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
|
||||
|
||||
|
||||
|
||||
@@ -294,7 +294,7 @@ Variable | Description | Required
|
||||
`name` \| `service` | The list of service name strings. | yes
|
||||
`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
|
||||
|
||||
@@ -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"
|
||||
@@ -352,6 +353,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
|
||||
=========
|
||||
@@ -395,6 +423,8 @@ Variable | Description | Required
|
||||
`random` | Generate a random user password | 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 +437,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 +445,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
|
||||
| `certificate` - Base-64 encoded user certificate, not usable with other certmapdata options. | no
|
||||
@@ -422,6 +454,10 @@ Variable | Description | Required
|
||||
| `subject` - Subject of the certificate, only usable together with `issuer` option. | no
|
||||
| `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
|
||||
`nomembers` | Suppress processing of membership attributes. (bool) | no
|
||||
|
||||
|
||||
@@ -442,3 +478,4 @@ Authors
|
||||
=======
|
||||
|
||||
Thomas Woerner
|
||||
Rafael Jeffman
|
||||
|
||||
14
playbooks/automount/automount-map-indirect-map.yml
Normal file
14
playbooks/automount/automount-map-indirect-map.yml
Normal 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
|
||||
12
playbooks/user/add-user-external-idp.yml
Normal file
12
playbooks/user/add-user-external-idp.yml
Normal 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
|
||||
17
playbooks/user/smb-attributes.yml
Normal file
17
playbooks/user/smb-attributes.yml
Normal 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:"
|
||||
@@ -30,7 +30,7 @@ __all__ = ["gssapi", "netaddr", "api", "ipalib_errors", "Env",
|
||||
"kinit_password", "kinit_keytab", "run", "DN", "VERSION",
|
||||
"paths", "tasks", "get_credentials_if_valid", "Encoding",
|
||||
"DNSName", "getargspec", "certificate_loader",
|
||||
"write_certificate_list"]
|
||||
"write_certificate_list", "boolean"]
|
||||
|
||||
import os
|
||||
# ansible-freeipa requires locale to be C, IPA requires utf-8.
|
||||
@@ -42,6 +42,7 @@ import tempfile
|
||||
import shutil
|
||||
import socket
|
||||
import base64
|
||||
import ast
|
||||
from datetime import datetime
|
||||
from contextlib import contextmanager
|
||||
from ansible.module_utils.basic import AnsibleModule
|
||||
@@ -49,6 +50,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+.
|
||||
@@ -1168,6 +1170,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):
|
||||
"""
|
||||
|
||||
@@ -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
|
||||
@@ -110,6 +129,35 @@ class AutomountMap(IPAAnsibleModule):
|
||||
else:
|
||||
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 = []
|
||||
name = self.params_get("name")
|
||||
@@ -118,15 +166,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 +194,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 +206,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 +323,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
|
||||
|
||||
@@ -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.
|
||||
@@ -425,6 +426,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),
|
||||
@@ -525,6 +527,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)
|
||||
|
||||
|
||||
@@ -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
|
||||
@@ -667,6 +667,15 @@ 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,
|
||||
single_host):
|
||||
@@ -776,7 +785,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"],
|
||||
@@ -919,6 +929,8 @@ def main():
|
||||
|
||||
# Check version specific settings
|
||||
|
||||
check_authind(ansible_module, auth_ind)
|
||||
|
||||
server_realm = ansible_module.ipa_get_realm()
|
||||
|
||||
commands = []
|
||||
@@ -961,6 +973,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")
|
||||
|
||||
@@ -45,82 +45,84 @@ options:
|
||||
required: false
|
||||
aliases: ["cn"]
|
||||
maxlife:
|
||||
description: Maximum password lifetime (in days)
|
||||
description: Maximum password lifetime (in days). (int or "")
|
||||
type: str
|
||||
required: false
|
||||
aliases: ["krbmaxpwdlife"]
|
||||
minlife:
|
||||
description: Minimum password lifetime (in hours)
|
||||
description: Minimum password lifetime (in hours). (int or "")
|
||||
type: str
|
||||
required: false
|
||||
aliases: ["krbminpwdlife"]
|
||||
history:
|
||||
description: Password history size
|
||||
description: Password history size. (int or "")
|
||||
type: str
|
||||
required: false
|
||||
aliases: ["krbpwdhistorylength"]
|
||||
minclasses:
|
||||
description: Minimum number of character classes
|
||||
description: Minimum number of character classes. (int or "")
|
||||
type: str
|
||||
required: false
|
||||
aliases: ["krbpwdmindiffchars"]
|
||||
minlength:
|
||||
description: Minimum length of password
|
||||
description: Minimum length of password. (int or "")
|
||||
type: str
|
||||
required: false
|
||||
aliases: ["krbpwdminlength"]
|
||||
priority:
|
||||
description: Priority of the policy (higher number means lower priority)
|
||||
description: >
|
||||
Priority of the policy (higher number means lower priority). (int or "")
|
||||
type: str
|
||||
required: false
|
||||
aliases: ["cospriority"]
|
||||
maxfail:
|
||||
description: Consecutive failures before lockout
|
||||
description: Consecutive failures before lockout. (int or "")
|
||||
type: str
|
||||
required: false
|
||||
aliases: ["krbpwdmaxfailure"]
|
||||
failinterval:
|
||||
description: Period after which failure count will be reset (seconds)
|
||||
description: >
|
||||
Period after which failure count will be reset (seconds). (int or "")
|
||||
type: str
|
||||
required: false
|
||||
aliases: ["krbpwdfailurecountinterval"]
|
||||
lockouttime:
|
||||
description: Period for which lockout is enforced (seconds)
|
||||
description: Period for which lockout is enforced (seconds). (int or "")
|
||||
type: str
|
||||
required: false
|
||||
aliases: ["krbpwdlockoutduration"]
|
||||
maxrepeat:
|
||||
description: >
|
||||
Maximum number of same consecutive characters.
|
||||
Requires IPA 4.9+
|
||||
Requires IPA 4.9+. (int or "")
|
||||
type: str
|
||||
required: false
|
||||
aliases: ["ipapwdmaxrepeat"]
|
||||
maxsequence:
|
||||
description: >
|
||||
The maximum length of monotonic character sequences (abcd).
|
||||
Requires IPA 4.9+
|
||||
Requires IPA 4.9+. (int or "")
|
||||
type: str
|
||||
required: false
|
||||
aliases: ["ipapwdmaxsequence"]
|
||||
dictcheck:
|
||||
description: >
|
||||
Check if the password is a dictionary word.
|
||||
Requires IPA 4.9+
|
||||
Requires IPA 4.9+. (bool or "")
|
||||
type: str
|
||||
required: false
|
||||
aliases: ["ipapwdictcheck"]
|
||||
usercheck:
|
||||
description: >
|
||||
Check if the password contains the username.
|
||||
Requires IPA 4.9+
|
||||
Requires IPA 4.9+. (bool or "")
|
||||
type: str
|
||||
required: false
|
||||
aliases: ["ipapwdusercheck"]
|
||||
gracelimit:
|
||||
description: >
|
||||
Number of LDAP authentications allowed after expiration.
|
||||
Requires IPA 4.10.1+
|
||||
Requires IPA 4.10.1+. (int or "")
|
||||
type: str
|
||||
required: false
|
||||
aliases: ["passwordgracelimit"]
|
||||
@@ -151,7 +153,7 @@ RETURN = """
|
||||
"""
|
||||
|
||||
from ansible.module_utils.ansible_freeipa_module import \
|
||||
IPAAnsibleModule, compare_args_ipa
|
||||
IPAAnsibleModule, compare_args_ipa, boolean
|
||||
|
||||
|
||||
def find_pwpolicy(module, name):
|
||||
@@ -359,17 +361,12 @@ def main():
|
||||
gracelimit = int_or_empty_param(gracelimit, "gracelimit")
|
||||
|
||||
def bool_or_empty_param(value, param): # pylint: disable=R1710
|
||||
# As of Ansible 2.14, values True, False, Yes an No, with variable
|
||||
# capitalization are accepted by Ansible.
|
||||
if not value:
|
||||
if value is None or value == "":
|
||||
return value
|
||||
if value in ["TRUE", "True", "true", "YES", "Yes", "yes"]:
|
||||
return True
|
||||
if value in ["FALSE", "False", "false", "NO", "No", "no"]:
|
||||
return False
|
||||
ansible_module.fail_json(
|
||||
msg="Invalid value '%s' for argument '%s'." % (value, param)
|
||||
)
|
||||
try:
|
||||
return boolean(value)
|
||||
except TypeError as terr:
|
||||
ansible_module.fail_json(msg="Param '%s': %s" % (param, str(terr)))
|
||||
|
||||
dictcheck = bool_or_empty_param(dictcheck, "dictcheck")
|
||||
usercheck = bool_or_empty_param(usercheck, "usercheck")
|
||||
|
||||
@@ -45,9 +45,9 @@ options:
|
||||
aliases: ["cn"]
|
||||
location:
|
||||
description: |
|
||||
The server location string.
|
||||
"" for location reset.
|
||||
Only in state: present.
|
||||
The server DNS location.
|
||||
Only available with 'state: present'.
|
||||
Use "" for location reset.
|
||||
type: str
|
||||
required: false
|
||||
aliases: ["ipalocation_location"]
|
||||
@@ -55,46 +55,46 @@ options:
|
||||
description: |
|
||||
Weight for server services
|
||||
Values 0 to 65535, -1 for weight reset.
|
||||
Only in state: present.
|
||||
Only available with 'state: present'.
|
||||
required: false
|
||||
type: int
|
||||
aliases: ["ipaserviceweight"]
|
||||
hidden:
|
||||
description: |
|
||||
Set hidden state of a server.
|
||||
Only in state: present.
|
||||
Only available with 'state: present'.
|
||||
required: false
|
||||
type: bool
|
||||
no_members:
|
||||
description: |
|
||||
Suppress processing of membership attributes
|
||||
Only in state: present.
|
||||
Only available with 'state: present'.
|
||||
required: false
|
||||
type: bool
|
||||
delete_continue:
|
||||
description: |
|
||||
Continuous mode: Don't stop on errors.
|
||||
Only in state: absent.
|
||||
Only available with 'state: absent'.
|
||||
required: false
|
||||
type: bool
|
||||
aliases: ["continue"]
|
||||
ignore_last_of_role:
|
||||
description: |
|
||||
Skip a check whether the last CA master or DNS server is removed.
|
||||
Only in state: absent.
|
||||
Only available with 'state: absent'.
|
||||
required: false
|
||||
type: bool
|
||||
ignore_topology_disconnect:
|
||||
description: |
|
||||
Ignore topology connectivity problems after removal.
|
||||
Only in state: absent.
|
||||
Only available with 'state: absent'.
|
||||
required: false
|
||||
type: bool
|
||||
force:
|
||||
description: |
|
||||
Force server removal even if it does not exist.
|
||||
Will always result in changed.
|
||||
Only in state: absent.
|
||||
Only available with 'state: absent'.
|
||||
required: false
|
||||
type: bool
|
||||
state:
|
||||
|
||||
@@ -74,7 +74,7 @@ options:
|
||||
type: list
|
||||
elements: str
|
||||
required: false
|
||||
choices: ["otp", "radius", "pkinit", "hardened", ""]
|
||||
choices: ["otp", "radius", "pkinit", "hardened", "idp", ""]
|
||||
aliases: ["krbprincipalauthind"]
|
||||
skip_host_check:
|
||||
description: Skip checking if host object exists.
|
||||
@@ -185,7 +185,7 @@ options:
|
||||
type: list
|
||||
elements: str
|
||||
required: false
|
||||
choices: ["otp", "radius", "pkinit", "hardened", ""]
|
||||
choices: ["otp", "radius", "pkinit", "hardened", "idp", ""]
|
||||
aliases: ["krbprincipalauthind"]
|
||||
skip_host_check:
|
||||
description: Skip checking if host object exists.
|
||||
@@ -491,6 +491,15 @@ def check_parameters(module, state, action, names):
|
||||
module.params_fail_used_invalid(invalid, state, action)
|
||||
|
||||
|
||||
def check_authind(module, auth_ind):
|
||||
_invalid = module.ipa_command_invalid_param_choices(
|
||||
"service_add", "krbprincipalauthind", auth_ind)
|
||||
if _invalid:
|
||||
module.fail_json(
|
||||
msg="The use of krbprincipalauthind '%s' is not supported "
|
||||
"by your IPA version" % "','".join(_invalid))
|
||||
|
||||
|
||||
def init_ansible_module():
|
||||
service_spec = dict(
|
||||
# service attributesstr
|
||||
@@ -506,7 +515,8 @@ def init_ansible_module():
|
||||
choices=["MS-PAC", "PAD", "NONE", ""]),
|
||||
auth_ind=dict(type="list", elements="str",
|
||||
aliases=["krbprincipalauthind"],
|
||||
choices=["otp", "radius", "pkinit", "hardened", ""]),
|
||||
choices=["otp", "radius", "pkinit", "hardened", "idp",
|
||||
""]),
|
||||
skip_host_check=dict(type="bool"),
|
||||
force=dict(type="bool"),
|
||||
requires_pre_auth=dict(
|
||||
@@ -642,6 +652,7 @@ def main():
|
||||
if skip_host_check and not has_skip_host_check:
|
||||
ansible_module.fail_json(
|
||||
msg="Skipping host check is not supported by your IPA version")
|
||||
check_authind(ansible_module, auth_ind)
|
||||
|
||||
commands = []
|
||||
keytab_members = ["user", "group", "host", "hostgroup"]
|
||||
@@ -664,6 +675,7 @@ def main():
|
||||
certificate = [cert.strip() for cert in certificate]
|
||||
pac_type = service.get("pac_type")
|
||||
auth_ind = service.get("auth_ind")
|
||||
check_authind(ansible_module, auth_ind)
|
||||
skip_host_check = service.get("skip_host_check")
|
||||
if skip_host_check and not has_skip_host_check:
|
||||
ansible_module.fail_json(
|
||||
|
||||
@@ -80,6 +80,10 @@ options:
|
||||
description: The home directory
|
||||
type: str
|
||||
required: false
|
||||
gecos:
|
||||
description: The GECOS
|
||||
type: str
|
||||
required: false
|
||||
shell:
|
||||
description: The login shell
|
||||
type: str
|
||||
@@ -133,6 +137,10 @@ options:
|
||||
type: int
|
||||
required: false
|
||||
aliases: ["gidnumber"]
|
||||
street:
|
||||
description: Street address
|
||||
type: str
|
||||
required: false
|
||||
city:
|
||||
description: City
|
||||
type: str
|
||||
@@ -200,7 +208,7 @@ options:
|
||||
Use empty string to reset userauthtype to the initial value.
|
||||
type: list
|
||||
elements: str
|
||||
choices: ['password', 'radius', 'otp', '']
|
||||
choices: ["password", "radius", "otp", "pkinit", "hardened", "idp", ""]
|
||||
required: false
|
||||
aliases: ["ipauserauthtype"]
|
||||
userclass:
|
||||
@@ -234,10 +242,45 @@ options:
|
||||
description: Employee Type
|
||||
type: str
|
||||
required: false
|
||||
smb_logon_script:
|
||||
description: SMB logon script path
|
||||
type: str
|
||||
required: false
|
||||
aliases: ["ipantlogonscript"]
|
||||
smb_profile_path:
|
||||
description: SMB profile path
|
||||
type: str
|
||||
required: false
|
||||
aliases: ["ipantprofilepath"]
|
||||
smb_home_dir:
|
||||
description: SMB Home Directory
|
||||
type: str
|
||||
required: false
|
||||
aliases: ["ipanthomedirectory"]
|
||||
smb_home_drive:
|
||||
description: SMB Home Directory Drive
|
||||
type: str
|
||||
required: false
|
||||
choices: [
|
||||
'A:', 'B:', 'C:', 'D:', 'E:', 'F:', 'G:', 'H:', 'I:', 'J:',
|
||||
'K:', 'L:', 'M:', 'N:', 'O:', 'P:', 'Q:', 'R:', 'S:', 'T:',
|
||||
'U:', 'V:', 'W:', 'X:', 'Y:', 'Z:', ''
|
||||
]
|
||||
aliases: ["ipanthomedirectorydrive"]
|
||||
preferredlanguage:
|
||||
description: Preferred Language
|
||||
type: str
|
||||
required: false
|
||||
idp:
|
||||
description: External IdP configuration
|
||||
type: str
|
||||
required: false
|
||||
aliases: ["ipaidpconfiglink"]
|
||||
idp_user_id:
|
||||
description: A string that identifies the user at external IdP
|
||||
type: str
|
||||
required: false
|
||||
aliases: ["ipaidpsub"]
|
||||
certificate:
|
||||
description: List of base-64 encoded user certificates
|
||||
type: list
|
||||
@@ -304,6 +347,10 @@ options:
|
||||
description: The home directory
|
||||
type: str
|
||||
required: false
|
||||
gecos:
|
||||
description: The GECOS
|
||||
type: str
|
||||
required: false
|
||||
shell:
|
||||
description: The login shell
|
||||
type: str
|
||||
@@ -357,6 +404,10 @@ options:
|
||||
type: int
|
||||
required: false
|
||||
aliases: ["gidnumber"]
|
||||
street:
|
||||
description: Street address
|
||||
type: str
|
||||
required: false
|
||||
city:
|
||||
description: City
|
||||
type: str
|
||||
@@ -424,7 +475,7 @@ options:
|
||||
Use empty string to reset userauthtype to the initial value.
|
||||
type: list
|
||||
elements: str
|
||||
choices: ['password', 'radius', 'otp', '']
|
||||
choices: ["password", "radius", "otp", "pkinit", "hardened", "idp", ""]
|
||||
required: false
|
||||
aliases: ["ipauserauthtype"]
|
||||
userclass:
|
||||
@@ -458,10 +509,45 @@ options:
|
||||
description: Employee Type
|
||||
type: str
|
||||
required: false
|
||||
smb_logon_script:
|
||||
description: SMB logon script path
|
||||
type: str
|
||||
required: false
|
||||
aliases: ["ipantlogonscript"]
|
||||
smb_profile_path:
|
||||
description: SMB profile path
|
||||
type: str
|
||||
required: false
|
||||
aliases: ["ipantprofilepath"]
|
||||
smb_home_dir:
|
||||
description: SMB Home Directory
|
||||
type: str
|
||||
required: false
|
||||
aliases: ["ipanthomedirectory"]
|
||||
smb_home_drive:
|
||||
description: SMB Home Directory Drive
|
||||
type: str
|
||||
required: false
|
||||
choices: [
|
||||
'A:', 'B:', 'C:', 'D:', 'E:', 'F:', 'G:', 'H:', 'I:', 'J:',
|
||||
'K:', 'L:', 'M:', 'N:', 'O:', 'P:', 'Q:', 'R:', 'S:', 'T:',
|
||||
'U:', 'V:', 'W:', 'X:', 'Y:', 'Z:', ''
|
||||
]
|
||||
aliases: ["ipanthomedirectorydrive"]
|
||||
preferredlanguage:
|
||||
description: Preferred Language
|
||||
type: str
|
||||
required: false
|
||||
idp:
|
||||
description: External IdP configuration
|
||||
type: str
|
||||
required: false
|
||||
aliases: ["ipaidpconfiglink"]
|
||||
idp_user_id:
|
||||
description: A string that identifies the user at external IdP
|
||||
type: str
|
||||
required: false
|
||||
aliases: ["ipaidpsub"]
|
||||
certificate:
|
||||
description: List of base-64 encoded user certificates
|
||||
type: list
|
||||
@@ -597,6 +683,17 @@ EXAMPLES = """
|
||||
ipaadmin_password: SomeADMINpassword
|
||||
name: pinky,brain
|
||||
state: disabled
|
||||
|
||||
# Ensure a user has 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:"
|
||||
"""
|
||||
|
||||
RETURN = """
|
||||
@@ -652,12 +749,14 @@ def find_user(module, name):
|
||||
return _result
|
||||
|
||||
|
||||
def gen_args(first, last, fullname, displayname, initials, homedir, shell,
|
||||
email, principalexpiration, passwordexpiration, password,
|
||||
random, uid, gid, city, userstate, postalcode, phone, mobile,
|
||||
pager, fax, orgunit, title, carlicense, sshpubkey, userauthtype,
|
||||
userclass, radius, radiususer, departmentnumber, employeenumber,
|
||||
employeetype, preferredlanguage, noprivate, nomembers):
|
||||
def gen_args(first, last, fullname, displayname, initials, homedir, gecos,
|
||||
shell, email, principalexpiration, passwordexpiration, password,
|
||||
random, uid, gid, street, city, userstate, postalcode, phone,
|
||||
mobile, pager, fax, orgunit, title, carlicense, sshpubkey,
|
||||
userauthtype, userclass, radius, radiususer, departmentnumber,
|
||||
employeenumber, employeetype, preferredlanguage, smb_logon_script,
|
||||
smb_profile_path, smb_home_dir, smb_home_drive, idp, idp_user_id,
|
||||
noprivate, nomembers):
|
||||
# principal, manager, certificate and certmapdata are handled not in here
|
||||
_args = {}
|
||||
if first is not None:
|
||||
@@ -672,6 +771,8 @@ def gen_args(first, last, fullname, displayname, initials, homedir, shell,
|
||||
_args["initials"] = initials
|
||||
if homedir is not None:
|
||||
_args["homedirectory"] = homedir
|
||||
if gecos is not None:
|
||||
_args["gecos"] = gecos
|
||||
if shell is not None:
|
||||
_args["loginshell"] = shell
|
||||
if email is not None and len(email) > 0:
|
||||
@@ -688,6 +789,8 @@ def gen_args(first, last, fullname, displayname, initials, homedir, shell,
|
||||
_args["uidnumber"] = to_text(str(uid))
|
||||
if gid is not None:
|
||||
_args["gidnumber"] = to_text(str(gid))
|
||||
if street is not None:
|
||||
_args["street"] = street
|
||||
if city is not None:
|
||||
_args["l"] = city
|
||||
if userstate is not None:
|
||||
@@ -726,48 +829,57 @@ def gen_args(first, last, fullname, displayname, initials, homedir, shell,
|
||||
_args["employeetype"] = employeetype
|
||||
if preferredlanguage is not None:
|
||||
_args["preferredlanguage"] = preferredlanguage
|
||||
if idp is not None:
|
||||
_args["ipaidpconfiglink"] = idp
|
||||
if idp_user_id is not None:
|
||||
_args["ipaidpsub"] = idp_user_id
|
||||
if noprivate is not None:
|
||||
_args["noprivate"] = noprivate
|
||||
if nomembers is not None:
|
||||
_args["no_members"] = nomembers
|
||||
if smb_logon_script is not None:
|
||||
_args["ipantlogonscript"] = smb_logon_script
|
||||
if smb_profile_path is not None:
|
||||
_args["ipantprofilepath"] = smb_profile_path
|
||||
if smb_home_dir is not None:
|
||||
_args["ipanthomedirectory"] = smb_home_dir
|
||||
if smb_home_drive is not None:
|
||||
_args["ipanthomedirectorydrive"] = smb_home_drive
|
||||
return _args
|
||||
|
||||
|
||||
def check_parameters( # pylint: disable=unused-argument
|
||||
module, state, action, first, last, fullname, displayname, initials,
|
||||
homedir, shell, email, principal, principalexpiration,
|
||||
passwordexpiration, password, random, uid, gid, city, phone, mobile,
|
||||
pager, fax, orgunit, title, manager, carlicense, sshpubkey,
|
||||
homedir, gecos, shell, email, principal, principalexpiration,
|
||||
passwordexpiration, password, random, uid, gid, street, city, phone,
|
||||
mobile, pager, fax, orgunit, title, manager, carlicense, sshpubkey,
|
||||
userauthtype, userclass, radius, radiususer, departmentnumber,
|
||||
employeenumber, employeetype, preferredlanguage, certificate,
|
||||
certmapdata, noprivate, nomembers, preserve, update_password):
|
||||
invalid = []
|
||||
if state == "present":
|
||||
if action == "member":
|
||||
invalid = ["first", "last", "fullname", "displayname", "initials",
|
||||
"homedir", "shell", "email", "principalexpiration",
|
||||
"passwordexpiration", "password", "random", "uid",
|
||||
"gid", "city", "phone", "mobile", "pager", "fax",
|
||||
"orgunit", "title", "carlicense", "sshpubkey",
|
||||
"userauthtype", "userclass", "radius", "radiususer",
|
||||
"departmentnumber", "employeenumber", "employeetype",
|
||||
"preferredlanguage", "noprivate", "nomembers",
|
||||
"preserve", "update_password"]
|
||||
|
||||
certmapdata, noprivate, nomembers, preserve, update_password,
|
||||
smb_logon_script, smb_profile_path, smb_home_dir, smb_home_drive,
|
||||
idp, ipa_user_id,
|
||||
):
|
||||
if state == "present" and action == "user":
|
||||
invalid = ["preserve"]
|
||||
else:
|
||||
invalid = ["first", "last", "fullname", "displayname", "initials",
|
||||
"homedir", "shell", "email", "principalexpiration",
|
||||
"passwordexpiration", "password", "random", "uid",
|
||||
"gid", "city", "phone", "mobile", "pager", "fax",
|
||||
"orgunit", "title", "carlicense", "sshpubkey",
|
||||
"userauthtype", "userclass", "radius", "radiususer",
|
||||
"departmentnumber", "employeenumber", "employeetype",
|
||||
"preferredlanguage", "noprivate", "nomembers",
|
||||
"update_password"]
|
||||
if action == "user":
|
||||
invalid.extend(["principal", "manager",
|
||||
"certificate", "certmapdata",
|
||||
])
|
||||
invalid = [
|
||||
"first", "last", "fullname", "displayname", "initials", "homedir",
|
||||
"shell", "email", "principalexpiration", "passwordexpiration",
|
||||
"password", "random", "uid", "gid", "street", "city", "phone",
|
||||
"mobile", "pager", "fax", "orgunit", "title", "carlicense",
|
||||
"sshpubkey", "userauthtype", "userclass", "radius", "radiususer",
|
||||
"departmentnumber", "employeenumber", "employeetype",
|
||||
"preferredlanguage", "noprivate", "nomembers", "update_password",
|
||||
"gecos", "smb_logon_script", "smb_profile_path", "smb_home_dir",
|
||||
"smb_home_drive", "idp", "idp_user_id"
|
||||
]
|
||||
|
||||
if state == "present" and action == "member":
|
||||
invalid.append("preserve")
|
||||
else:
|
||||
if action == "user":
|
||||
invalid.extend(
|
||||
["principal", "manager", "certificate", "certmapdata"])
|
||||
|
||||
if state != "absent" and preserve is not None:
|
||||
module.fail_json(
|
||||
@@ -801,6 +913,15 @@ def check_parameters( # pylint: disable=unused-argument
|
||||
module.fail_json(msg="certmapdata: subject is missing")
|
||||
|
||||
|
||||
def check_userauthtype(module, userauthtype):
|
||||
_invalid = module.ipa_command_invalid_param_choices(
|
||||
"user_add", "ipauserauthtype", userauthtype)
|
||||
if _invalid:
|
||||
module.fail_json(
|
||||
msg="The use of userauthtype '%s' is not supported "
|
||||
"by your IPA version" % "','".join(_invalid))
|
||||
|
||||
|
||||
def extend_emails(email, default_email_domain):
|
||||
if email is not None:
|
||||
return ["%s@%s" % (_email, default_email_domain)
|
||||
@@ -902,6 +1023,7 @@ def main():
|
||||
displayname=dict(type="str", default=None),
|
||||
initials=dict(type="str", default=None),
|
||||
homedir=dict(type="str", default=None),
|
||||
gecos=dict(type="str", default=None),
|
||||
shell=dict(type="str", aliases=["loginshell"], default=None),
|
||||
email=dict(type="list", elements="str", default=None),
|
||||
principal=dict(type="list", elements="str",
|
||||
@@ -917,6 +1039,7 @@ def main():
|
||||
random=dict(type='bool', default=None),
|
||||
uid=dict(type="int", aliases=["uidnumber"], default=None),
|
||||
gid=dict(type="int", aliases=["gidnumber"], default=None),
|
||||
street=dict(type="str", default=None),
|
||||
city=dict(type="str", default=None),
|
||||
userstate=dict(type="str", aliases=["st"], default=None),
|
||||
postalcode=dict(type="str", aliases=["zip"], default=None),
|
||||
@@ -934,7 +1057,8 @@ def main():
|
||||
default=None),
|
||||
userauthtype=dict(type='list', elements="str",
|
||||
aliases=["ipauserauthtype"], default=None,
|
||||
choices=['password', 'radius', 'otp', '']),
|
||||
choices=["password", "radius", "otp", "pkinit",
|
||||
"hardened", "idp", ""]),
|
||||
userclass=dict(type="list", elements="str", aliases=["class"],
|
||||
default=None),
|
||||
radius=dict(type="str", aliases=["ipatokenradiusconfiglink"],
|
||||
@@ -945,6 +1069,17 @@ def main():
|
||||
departmentnumber=dict(type="list", elements="str", default=None),
|
||||
employeenumber=dict(type="str", default=None),
|
||||
employeetype=dict(type="str", default=None),
|
||||
smb_logon_script=dict(type="str", default=None,
|
||||
aliases=["ipantlogonscript"]),
|
||||
smb_profile_path=dict(type="str", default=None,
|
||||
aliases=["ipantprofilepath"]),
|
||||
smb_home_dir=dict(type="str", default=None,
|
||||
aliases=["ipanthomedirectory"]),
|
||||
smb_home_drive=dict(type="str", default=None,
|
||||
choices=[
|
||||
("%c:" % chr(x))
|
||||
for x in range(ord('A'), ord('Z') + 1)
|
||||
] + [""], aliases=["ipanthomedirectorydrive"]),
|
||||
preferredlanguage=dict(type="str", default=None),
|
||||
certificate=dict(type="list", elements="str",
|
||||
aliases=["usercertificate"], default=None),
|
||||
@@ -959,6 +1094,9 @@ def main():
|
||||
elements='dict', required=False),
|
||||
noprivate=dict(type='bool', default=None),
|
||||
nomembers=dict(type='bool', default=None),
|
||||
idp=dict(type="str", default=None, aliases=['ipaidpconfiglink']),
|
||||
idp_user_id=dict(type="str", default=None,
|
||||
aliases=['ipaidpconfiglink']),
|
||||
)
|
||||
|
||||
ansible_module = IPAAnsibleModule(
|
||||
@@ -1015,6 +1153,7 @@ def main():
|
||||
displayname = ansible_module.params_get("displayname")
|
||||
initials = ansible_module.params_get("initials")
|
||||
homedir = ansible_module.params_get("homedir")
|
||||
gecos = ansible_module.params_get("gecos")
|
||||
shell = ansible_module.params_get("shell")
|
||||
email = ansible_module.params_get("email")
|
||||
principal = ansible_module.params_get("principal")
|
||||
@@ -1033,6 +1172,7 @@ def main():
|
||||
random = ansible_module.params_get("random")
|
||||
uid = ansible_module.params_get("uid")
|
||||
gid = ansible_module.params_get("gid")
|
||||
street = ansible_module.params_get("street")
|
||||
city = ansible_module.params_get("city")
|
||||
userstate = ansible_module.params_get("userstate")
|
||||
postalcode = ansible_module.params_get("postalcode")
|
||||
@@ -1055,6 +1195,12 @@ def main():
|
||||
employeenumber = ansible_module.params_get("employeenumber")
|
||||
employeetype = ansible_module.params_get("employeetype")
|
||||
preferredlanguage = ansible_module.params_get("preferredlanguage")
|
||||
smb_logon_script = ansible_module.params_get("smb_logon_script")
|
||||
smb_profile_path = ansible_module.params_get("smb_profile_path")
|
||||
smb_home_dir = ansible_module.params_get("smb_home_dir")
|
||||
smb_home_drive = ansible_module.params_get("smb_home_drive")
|
||||
idp = ansible_module.params_get("idp")
|
||||
idp_user_id = ansible_module.params_get("idp_user_id")
|
||||
certificate = ansible_module.params_get("certificate")
|
||||
certmapdata = ansible_module.params_get("certmapdata")
|
||||
noprivate = ansible_module.params_get("noprivate")
|
||||
@@ -1080,13 +1226,15 @@ def main():
|
||||
|
||||
check_parameters(
|
||||
ansible_module, state, action,
|
||||
first, last, fullname, displayname, initials, homedir, shell, email,
|
||||
first, last, fullname, displayname, initials, homedir, gecos, shell,
|
||||
email,
|
||||
principal, principalexpiration, passwordexpiration, password, random,
|
||||
uid, gid, city, phone, mobile, pager, fax, orgunit, title, manager,
|
||||
carlicense, sshpubkey, userauthtype, userclass, radius, radiususer,
|
||||
departmentnumber, employeenumber, employeetype, preferredlanguage,
|
||||
certificate, certmapdata, noprivate, nomembers, preserve,
|
||||
update_password)
|
||||
uid, gid, street, city, phone, mobile, pager, fax, orgunit, title,
|
||||
manager, carlicense, sshpubkey, userauthtype, userclass, radius,
|
||||
radiususer, departmentnumber, employeenumber, employeetype,
|
||||
preferredlanguage, certificate, certmapdata, noprivate, nomembers,
|
||||
preserve, update_password, smb_logon_script, smb_profile_path,
|
||||
smb_home_dir, smb_home_drive, idp, idp_user_id)
|
||||
certmapdata = convert_certmapdata(certmapdata)
|
||||
|
||||
# Use users if names is None
|
||||
@@ -1105,6 +1253,10 @@ def main():
|
||||
|
||||
server_realm = ansible_module.ipa_get_realm()
|
||||
|
||||
# Check API specific parameters
|
||||
|
||||
check_userauthtype(ansible_module, userauthtype)
|
||||
|
||||
# Default email domain
|
||||
|
||||
result = ansible_module.ipa_command_no_name("config_show", {})
|
||||
@@ -1133,6 +1285,7 @@ def main():
|
||||
displayname = user.get("displayname")
|
||||
initials = user.get("initials")
|
||||
homedir = user.get("homedir")
|
||||
gecos = user.get("gecos")
|
||||
shell = user.get("shell")
|
||||
email = user.get("email")
|
||||
principal = user.get("principal")
|
||||
@@ -1150,6 +1303,7 @@ def main():
|
||||
random = user.get("random")
|
||||
uid = user.get("uid")
|
||||
gid = user.get("gid")
|
||||
street = user.get("street")
|
||||
city = user.get("city")
|
||||
userstate = user.get("userstate")
|
||||
postalcode = user.get("postalcode")
|
||||
@@ -1170,6 +1324,12 @@ def main():
|
||||
employeenumber = user.get("employeenumber")
|
||||
employeetype = user.get("employeetype")
|
||||
preferredlanguage = user.get("preferredlanguage")
|
||||
smb_logon_script = user.get("smb_logon_script")
|
||||
smb_profile_path = user.get("smb_profile_path")
|
||||
smb_home_dir = user.get("smb_home_dir")
|
||||
smb_home_drive = user.get("smb_home_drive")
|
||||
idp = user.get("idp")
|
||||
idp_user_id = user.get("idp_user_id")
|
||||
certificate = user.get("certificate")
|
||||
certmapdata = user.get("certmapdata")
|
||||
noprivate = user.get("noprivate")
|
||||
@@ -1178,16 +1338,21 @@ def main():
|
||||
check_parameters(
|
||||
ansible_module, state, action,
|
||||
first, last, fullname, displayname, initials, homedir,
|
||||
shell, email, principal, principalexpiration,
|
||||
passwordexpiration, password, random, uid, gid, city,
|
||||
phone, mobile, pager, fax, orgunit, title, manager,
|
||||
gecos, shell, email, principal, principalexpiration,
|
||||
passwordexpiration, password, random, uid, gid, street,
|
||||
city, phone, mobile, pager, fax, orgunit, title, manager,
|
||||
carlicense, sshpubkey, userauthtype, userclass, radius,
|
||||
radiususer, departmentnumber, employeenumber,
|
||||
employeetype, preferredlanguage, certificate,
|
||||
certmapdata, noprivate, nomembers, preserve,
|
||||
update_password)
|
||||
update_password, smb_logon_script, smb_profile_path,
|
||||
smb_home_dir, smb_home_drive, idp, idp_user_id)
|
||||
certmapdata = convert_certmapdata(certmapdata)
|
||||
|
||||
# Check API specific parameters
|
||||
|
||||
check_userauthtype(ansible_module, userauthtype)
|
||||
|
||||
# Extend email addresses
|
||||
|
||||
email = extend_emails(email, default_email_domain)
|
||||
@@ -1227,6 +1392,34 @@ def main():
|
||||
msg="The use of certmapdata is not supported by "
|
||||
"your IPA version")
|
||||
|
||||
# Check if SMB attributes are available
|
||||
if (
|
||||
any([
|
||||
smb_logon_script, smb_profile_path, smb_home_dir,
|
||||
smb_home_drive
|
||||
])
|
||||
and not ansible_module.ipa_command_param_exists(
|
||||
"user_mod", "ipanthomedirectory"
|
||||
)
|
||||
):
|
||||
ansible_module.fail_json(
|
||||
msg="The use of smb_logon_script, smb_profile_path, "
|
||||
"smb_profile_path, and smb_home_drive is not supported "
|
||||
"by your IPA version")
|
||||
|
||||
# Check if IdP support is available
|
||||
require_idp = (
|
||||
idp is not None
|
||||
or idp_user_id is not None
|
||||
or userauthtype == "idp"
|
||||
)
|
||||
has_idp_support = ansible_module.ipa_command_param_exists(
|
||||
"user_add", "ipaidpconfiglink"
|
||||
)
|
||||
if require_idp and not has_idp_support:
|
||||
ansible_module.fail_json(
|
||||
msg="Your IPA version does not support External IdP.")
|
||||
|
||||
# Make sure user exists
|
||||
res_find = find_user(ansible_module, name)
|
||||
|
||||
@@ -1235,12 +1428,16 @@ def main():
|
||||
# Generate args
|
||||
args = gen_args(
|
||||
first, last, fullname, displayname, initials, homedir,
|
||||
gecos,
|
||||
shell, email, principalexpiration, passwordexpiration,
|
||||
password, random, uid, gid, city, userstate, postalcode,
|
||||
phone, mobile, pager, fax, orgunit, title, carlicense,
|
||||
sshpubkey, userauthtype, userclass, radius, radiususer,
|
||||
departmentnumber, employeenumber, employeetype,
|
||||
preferredlanguage, noprivate, nomembers)
|
||||
password, random, uid, gid, street, city, userstate,
|
||||
postalcode, phone, mobile, pager, fax, orgunit, title,
|
||||
carlicense, sshpubkey, userauthtype, userclass, radius,
|
||||
radiususer, departmentnumber, employeenumber, employeetype,
|
||||
preferredlanguage, smb_logon_script, smb_profile_path,
|
||||
smb_home_dir, smb_home_drive, idp, idp_user_id, noprivate,
|
||||
nomembers,
|
||||
)
|
||||
|
||||
if action == "user":
|
||||
# Found the user
|
||||
@@ -1276,8 +1473,21 @@ def main():
|
||||
ansible_module.fail_json(
|
||||
msg="Last name is needed")
|
||||
|
||||
smb_attrs = {
|
||||
k: args[k]
|
||||
for k in [
|
||||
"ipanthomedirectory",
|
||||
"ipanthomedirectorydrive",
|
||||
"ipantlogonscript",
|
||||
"ipantprofilepath",
|
||||
]
|
||||
if k in args
|
||||
}
|
||||
for key in smb_attrs.keys():
|
||||
del args[key]
|
||||
commands.append([name, "user_add", args])
|
||||
|
||||
if smb_attrs:
|
||||
commands.append([name, "user_mod", smb_attrs])
|
||||
# Handle members: principal, manager, certificate and
|
||||
# certmapdata
|
||||
if res_find is not None:
|
||||
|
||||
@@ -34,7 +34,7 @@ Supported Distributions
|
||||
|
||||
* RHEL/CentOS 7.6+
|
||||
* Fedora 26+
|
||||
* Ubuntu
|
||||
* Ubuntu 16.04 and 18.04
|
||||
|
||||
|
||||
Requirements
|
||||
|
||||
@@ -28,7 +28,7 @@ Supported Distributions
|
||||
|
||||
* RHEL/CentOS 7.6+
|
||||
* Fedora 26+
|
||||
* Ubuntu
|
||||
* Ubuntu 16.04 and 18.04
|
||||
|
||||
|
||||
Requirements
|
||||
|
||||
@@ -25,7 +25,7 @@ Supported Distributions
|
||||
|
||||
* RHEL/CentOS 7.6+
|
||||
* Fedora 26+
|
||||
* Ubuntu
|
||||
* Ubuntu 16.04 and 18.04
|
||||
|
||||
|
||||
Requirements
|
||||
@@ -179,7 +179,7 @@ ipaserver_domain=example.com
|
||||
ipaserver_realm=EXAMPLE.COM
|
||||
ipaadmin_password=MySecretPassword123
|
||||
ipadm_password=MySecretPassword234
|
||||
ipaserver_random_serial_number=true
|
||||
ipaserver_random_serial_numbers=true
|
||||
```
|
||||
|
||||
By setting the variable in the inventory file, the same ipaserver deployment playbook, shown before, can be used.
|
||||
|
||||
143
tests/automount/test_automountmap_indirect.yml
Normal file
143
tests/automount/test_automountmap_indirect.yml
Normal file
@@ -0,0 +1,143 @@
|
||||
---
|
||||
- name: Test automountmap
|
||||
hosts: "{{ ipa_test_host | default('ipaserver') }}"
|
||||
become: no
|
||||
gather_facts: no
|
||||
|
||||
tasks:
|
||||
# setup environment
|
||||
- name: Ensure test maps are absent
|
||||
ipaautomountmap:
|
||||
ipaadmin_password: SomeADMINpassword
|
||||
location: TestIndirect
|
||||
name:
|
||||
- DirectMap
|
||||
- IndirectMap
|
||||
- IndirectMapDefault
|
||||
- IndirectMapDefaultAbsolute
|
||||
state: absent
|
||||
|
||||
- name: Ensure test location is present
|
||||
ipaautomountlocation:
|
||||
ipaadmin_password: SomeADMINpassword
|
||||
name: TestIndirect
|
||||
|
||||
- name: Ensure parent map is present
|
||||
ipaautomountmap:
|
||||
ipaadmin_password: SomeADMINpassword
|
||||
location: TestIndirect
|
||||
name: DirectMap
|
||||
|
||||
# TESTS
|
||||
- name: Mount point cannot start with '/' if parentmap is not 'auto.master'
|
||||
ipaautomountmap:
|
||||
ipaadmin_password: SomeADMINpassword
|
||||
location: TestIndirect
|
||||
name: IndirectMap
|
||||
parentmap: DirectMap
|
||||
mount: '/absolute/path/will/fail'
|
||||
register: result
|
||||
failed_when: not result.failed or 'mount point is relative to parent map' not in result.msg
|
||||
|
||||
- name: Ensure indirect map is present
|
||||
ipaautomountmap:
|
||||
ipaadmin_password: SomeADMINpassword
|
||||
location: TestIndirect
|
||||
name: IndirectMap
|
||||
parentmap: DirectMap
|
||||
mount: indirect
|
||||
register: result
|
||||
failed_when: result.failed or not result.changed
|
||||
|
||||
- name: Ensure indirect map is present, again
|
||||
ipaautomountmap:
|
||||
ipaadmin_password: SomeADMINpassword
|
||||
location: TestIndirect
|
||||
name: IndirectMap
|
||||
parentmap: DirectMap
|
||||
mount: indirect
|
||||
register: result
|
||||
failed_when: result.failed or result.changed
|
||||
|
||||
- name: Ensure indirect map is absent
|
||||
ipaautomountmap:
|
||||
ipaadmin_password: SomeADMINpassword
|
||||
location: TestIndirect
|
||||
name: IndirectMap
|
||||
state: absent
|
||||
register: result
|
||||
failed_when: result.failed or not result.changed
|
||||
|
||||
- name: Ensure indirect map is absent, again
|
||||
ipaautomountmap:
|
||||
ipaadmin_password: SomeADMINpassword
|
||||
location: TestIndirect
|
||||
name: IndirectMap
|
||||
state: absent
|
||||
register: result
|
||||
failed_when: result.failed or result.changed
|
||||
|
||||
- name: Ensure indirect map is present, after being deleted
|
||||
ipaautomountmap:
|
||||
ipaadmin_password: SomeADMINpassword
|
||||
location: TestIndirect
|
||||
name: IndirectMap
|
||||
parentmap: DirectMap
|
||||
mount: indirect
|
||||
register: result
|
||||
failed_when: result.failed or not result.changed
|
||||
|
||||
- name: Ensure indirect map is present, after being deleted, again
|
||||
ipaautomountmap:
|
||||
ipaadmin_password: SomeADMINpassword
|
||||
location: TestIndirect
|
||||
name: IndirectMap
|
||||
parentmap: DirectMap
|
||||
mount: indirect
|
||||
register: result
|
||||
failed_when: result.failed or result.changed
|
||||
|
||||
- name: Ensure indirect map is present with default parent (auto.master)
|
||||
ipaautomountmap:
|
||||
ipaadmin_password: SomeADMINpassword
|
||||
location: TestIndirect
|
||||
name: IndirectMapDefault
|
||||
mount: indirect_with_default
|
||||
register: result
|
||||
failed_when: result.failed or not result.changed
|
||||
|
||||
- name: Ensure indirect map is present with default parent (auto.master), again
|
||||
ipaautomountmap:
|
||||
ipaadmin_password: SomeADMINpassword
|
||||
location: TestIndirect
|
||||
name: IndirectMapDefault
|
||||
mount: indirect_with_default
|
||||
register: result
|
||||
failed_when: result.failed or result.changed
|
||||
|
||||
- name: Absolute paths must workd with 'auto.master'
|
||||
ipaautomountmap:
|
||||
ipaadmin_password: SomeADMINpassword
|
||||
location: TestIndirect
|
||||
name: IndirectMapDefaultAbsolute
|
||||
mount: /valid/path/indirect_with_default
|
||||
register: result
|
||||
failed_when: result.failed or not result.changed
|
||||
|
||||
# Cleanup
|
||||
- name: Ensure test maps are absent
|
||||
ipaautomountmap:
|
||||
ipaadmin_password: SomeADMINpassword
|
||||
location: TestIndirect
|
||||
name:
|
||||
- DirectMap
|
||||
- IndirectMap
|
||||
- IndirectMapDefault
|
||||
- IndirectMapDefaultAbsolute
|
||||
state: absent
|
||||
|
||||
- name: Ensure test location is absent
|
||||
ipaautomountlocation:
|
||||
ipaadmin_password: SomeADMINpassword
|
||||
name: TestIndirect
|
||||
state: absent
|
||||
@@ -27,7 +27,7 @@ stages:
|
||||
parameters:
|
||||
build_number: $(Build.BuildNumber)
|
||||
scenario: fedora-latest
|
||||
ansible_version: "-core >=2.13,<2.14"
|
||||
ansible_version: "-core >=2.14,<2.15"
|
||||
|
||||
# Galaxy on Fedora
|
||||
|
||||
@@ -38,7 +38,7 @@ stages:
|
||||
parameters:
|
||||
build_number: $(Build.BuildNumber)
|
||||
scenario: fedora-latest
|
||||
ansible_version: "-core >=2.13,<2.14"
|
||||
ansible_version: "-core >=2.14,<2.15"
|
||||
|
||||
# CentOS 9 Stream
|
||||
|
||||
@@ -49,7 +49,7 @@ stages:
|
||||
parameters:
|
||||
build_number: $(Build.BuildNumber)
|
||||
scenario: c9s
|
||||
ansible_version: "-core >=2.13,<2.14"
|
||||
ansible_version: "-core >=2.14,<2.15"
|
||||
|
||||
# CentOS 8 Stream
|
||||
|
||||
@@ -60,7 +60,7 @@ stages:
|
||||
parameters:
|
||||
build_number: $(Build.BuildNumber)
|
||||
scenario: c8s
|
||||
ansible_version: "-core >=2.13,<2.14"
|
||||
ansible_version: "-core >=2.14,<2.15"
|
||||
|
||||
# CentOS 7
|
||||
|
||||
@@ -71,4 +71,4 @@ stages:
|
||||
parameters:
|
||||
build_number: $(Build.BuildNumber)
|
||||
scenario: centos-7
|
||||
ansible_version: "-core >=2.13,<2.14"
|
||||
ansible_version: "-core >=2.14,<2.15"
|
||||
|
||||
@@ -16,15 +16,6 @@ stages:
|
||||
|
||||
# Fedora
|
||||
|
||||
- stage: FedoraLatest_Ansible_Core_2_12
|
||||
dependsOn: []
|
||||
jobs:
|
||||
- template: templates/group_tests.yml
|
||||
parameters:
|
||||
build_number: $(Build.BuildNumber)
|
||||
scenario: fedora-latest
|
||||
ansible_version: "-core >=2.12,<2.13"
|
||||
|
||||
- stage: FedoraLatest_Ansible_Core_2_13
|
||||
dependsOn: []
|
||||
jobs:
|
||||
@@ -43,6 +34,15 @@ stages:
|
||||
scenario: fedora-latest
|
||||
ansible_version: "-core >=2.14,<2.15"
|
||||
|
||||
- stage: FedoraLatest_Ansible_Core_2_15
|
||||
dependsOn: []
|
||||
jobs:
|
||||
- template: templates/group_tests.yml
|
||||
parameters:
|
||||
build_number: $(Build.BuildNumber)
|
||||
scenario: fedora-latest
|
||||
ansible_version: "-core >=2.15,<2.16"
|
||||
|
||||
- stage: FedoraLatest_Ansible_latest
|
||||
dependsOn: []
|
||||
jobs:
|
||||
@@ -54,15 +54,6 @@ stages:
|
||||
|
||||
# Galaxy on Fedora
|
||||
|
||||
- stage: Galaxy_FedoraLatest_Ansible_Core_2_12
|
||||
dependsOn: []
|
||||
jobs:
|
||||
- template: templates/galaxy_tests.yml
|
||||
parameters:
|
||||
build_number: $(Build.BuildNumber)
|
||||
scenario: fedora-latest
|
||||
ansible_version: "-core >=2.12,<2.13"
|
||||
|
||||
- stage: Galaxy_FedoraLatest_Ansible_Core_2_13
|
||||
dependsOn: []
|
||||
jobs:
|
||||
@@ -81,6 +72,15 @@ stages:
|
||||
scenario: fedora-latest
|
||||
ansible_version: "-core >=2.14,<2.15"
|
||||
|
||||
- stage: Galaxy_FedoraLatest_Ansible_Core_2_15
|
||||
dependsOn: []
|
||||
jobs:
|
||||
- template: templates/galaxy_tests.yml
|
||||
parameters:
|
||||
build_number: $(Build.BuildNumber)
|
||||
scenario: fedora-latest
|
||||
ansible_version: "-core >=2.15,<2.16"
|
||||
|
||||
- stage: Galaxy_FedoraLatest_Ansible_latest
|
||||
dependsOn: []
|
||||
jobs:
|
||||
@@ -92,15 +92,6 @@ stages:
|
||||
|
||||
# Fedora Rawhide
|
||||
|
||||
- stage: FedoraRawhide_Ansible_Core_2_12
|
||||
dependsOn: []
|
||||
jobs:
|
||||
- template: templates/group_tests.yml
|
||||
parameters:
|
||||
build_number: $(Build.BuildNumber)
|
||||
scenario: fedora-rawhide
|
||||
ansible_version: "-core >=2.12,<2.13"
|
||||
|
||||
- stage: FedoraRawhide_Ansible_Core_2_13
|
||||
dependsOn: []
|
||||
jobs:
|
||||
@@ -119,6 +110,15 @@ stages:
|
||||
scenario: fedora-rawhide
|
||||
ansible_version: "-core >=2.14,<2.15"
|
||||
|
||||
- stage: FedoraRawhide_Ansible_Core_2_15
|
||||
dependsOn: []
|
||||
jobs:
|
||||
- template: templates/group_tests.yml
|
||||
parameters:
|
||||
build_number: $(Build.BuildNumber)
|
||||
scenario: fedora-rawhide
|
||||
ansible_version: "-core >=2.15,<2.16"
|
||||
|
||||
- stage: FedoraRawhide_Ansible_latest
|
||||
dependsOn: []
|
||||
jobs:
|
||||
@@ -130,15 +130,6 @@ stages:
|
||||
|
||||
# CentoOS 9 Stream
|
||||
|
||||
- stage: c9s_Ansible_Core_2_12
|
||||
dependsOn: []
|
||||
jobs:
|
||||
- template: templates/group_tests.yml
|
||||
parameters:
|
||||
build_number: $(Build.BuildNumber)
|
||||
scenario: c9s
|
||||
ansible_version: "-core >=2.12,<2.13"
|
||||
|
||||
- stage: c9s_Ansible_Core_2_13
|
||||
dependsOn: []
|
||||
jobs:
|
||||
@@ -157,6 +148,15 @@ stages:
|
||||
scenario: c9s
|
||||
ansible_version: "-core >=2.14,<2.15"
|
||||
|
||||
- stage: c9s_Ansible_Core_2_15
|
||||
dependsOn: []
|
||||
jobs:
|
||||
- template: templates/group_tests.yml
|
||||
parameters:
|
||||
build_number: $(Build.BuildNumber)
|
||||
scenario: c9s
|
||||
ansible_version: "-core >=2.15,<2.16"
|
||||
|
||||
- stage: c9s_Ansible_latest
|
||||
dependsOn: []
|
||||
jobs:
|
||||
@@ -168,15 +168,6 @@ stages:
|
||||
|
||||
# CentOS 8 Stream
|
||||
|
||||
- stage: c8s_Ansible_Core_2_12
|
||||
dependsOn: []
|
||||
jobs:
|
||||
- template: templates/group_tests.yml
|
||||
parameters:
|
||||
build_number: $(Build.BuildNumber)
|
||||
scenario: c8s
|
||||
ansible_version: "-core >=2.12,<2.13"
|
||||
|
||||
- stage: c8s_Ansible_Core_2_13
|
||||
dependsOn: []
|
||||
jobs:
|
||||
@@ -195,6 +186,15 @@ stages:
|
||||
scenario: c8s
|
||||
ansible_version: "-core >=2.14,<2.15"
|
||||
|
||||
- stage: c8s_Ansible_Core_2_15
|
||||
dependsOn: []
|
||||
jobs:
|
||||
- template: templates/group_tests.yml
|
||||
parameters:
|
||||
build_number: $(Build.BuildNumber)
|
||||
scenario: c8s
|
||||
ansible_version: "-core >=2.15,<2.16"
|
||||
|
||||
- stage: c8s_Ansible_latest
|
||||
dependsOn: []
|
||||
jobs:
|
||||
@@ -206,15 +206,6 @@ stages:
|
||||
|
||||
# CentOS 7
|
||||
|
||||
- stage: CentOS7_Ansible_Core_2_12
|
||||
dependsOn: []
|
||||
jobs:
|
||||
- template: templates/group_tests.yml
|
||||
parameters:
|
||||
build_number: $(Build.BuildNumber)
|
||||
scenario: centos-7
|
||||
ansible_version: "-core >=2.12,<2.13"
|
||||
|
||||
- stage: CentOS7_Ansible_Core_2_13
|
||||
dependsOn: []
|
||||
jobs:
|
||||
@@ -233,6 +224,15 @@ stages:
|
||||
scenario: centos-7
|
||||
ansible_version: "-core >=2.14,<2.15"
|
||||
|
||||
- stage: CentOS7_Ansible_Core_2_15
|
||||
dependsOn: []
|
||||
jobs:
|
||||
- template: templates/group_tests.yml
|
||||
parameters:
|
||||
build_number: $(Build.BuildNumber)
|
||||
scenario: centos-7
|
||||
ansible_version: "-core >=2.15,<2.16"
|
||||
|
||||
- stage: CentOS7_Ansible_latest
|
||||
dependsOn: []
|
||||
jobs:
|
||||
|
||||
@@ -16,7 +16,7 @@ stages:
|
||||
parameters:
|
||||
build_number: $(Build.BuildNumber)
|
||||
scenario: fedora-latest
|
||||
ansible_version: "-core >=2.12,<2.13"
|
||||
ansible_version: "-core >=2.14,<2.15"
|
||||
|
||||
# Galaxy on Fedora
|
||||
|
||||
@@ -27,7 +27,7 @@ stages:
|
||||
parameters:
|
||||
build_number: $(Build.BuildNumber)
|
||||
scenario: fedora-latest
|
||||
ansible_version: "-core >=2.12,<2.13"
|
||||
ansible_version: "-core >=2.14,<2.15"
|
||||
|
||||
# CentOS 9 Stream
|
||||
|
||||
@@ -38,7 +38,7 @@ stages:
|
||||
parameters:
|
||||
build_number: $(Build.BuildNumber)
|
||||
scenario: c9s
|
||||
ansible_version: "-core >=2.13,<2.14"
|
||||
ansible_version: "-core >=2.14,<2.15"
|
||||
|
||||
# CentOS 8 Stream
|
||||
|
||||
@@ -49,7 +49,7 @@ stages:
|
||||
parameters:
|
||||
build_number: $(Build.BuildNumber)
|
||||
scenario: c8s
|
||||
ansible_version: "-core >=2.13,<2.14"
|
||||
ansible_version: "-core >=2.14,<2.15"
|
||||
|
||||
# CentOS 7
|
||||
|
||||
@@ -60,7 +60,7 @@ stages:
|
||||
parameters:
|
||||
build_number: $(Build.BuildNumber)
|
||||
scenario: centos-7
|
||||
ansible_version: "-core >=2.13,<2.14"
|
||||
ansible_version: "-core >=2.14,<2.15"
|
||||
|
||||
# Rawhide
|
||||
|
||||
@@ -71,4 +71,4 @@ stages:
|
||||
parameters:
|
||||
build_number: $(Build.BuildNumber)
|
||||
scenario: fedora-rawhide
|
||||
ansible_version: "-core >=2.13,<2.14"
|
||||
ansible_version: "-core >=2.14,<2.15"
|
||||
|
||||
@@ -56,6 +56,7 @@ jobs:
|
||||
env:
|
||||
IPA_SERVER_HOST: ${{ parameters.scenario }}
|
||||
RUN_TESTS_IN_DOCKER: true
|
||||
IPA_VERBOSITY: "-vvv"
|
||||
|
||||
- task: PublishTestResults@2
|
||||
inputs:
|
||||
|
||||
@@ -76,6 +76,7 @@ jobs:
|
||||
IPA_DISABLED_TESTS: ${{ variables.ipa_disabled_tests }}
|
||||
IPA_ENABLED_MODULES: ${{ variables.ipa_enabled_modules }}
|
||||
IPA_ENABLED_TESTS: ${{ variables.ipa_enabled_tests }}
|
||||
IPA_VERBOSITY: "-vvv"
|
||||
|
||||
- task: PublishTestResults@2
|
||||
inputs:
|
||||
|
||||
@@ -82,6 +82,7 @@ jobs:
|
||||
RUN_TESTS_IN_DOCKER: true
|
||||
IPA_DISABLED_MODULES: ${{ variables.ipa_disabled_modules }}
|
||||
IPA_DISABLED_TESTS: ${{ variables.ipa_disabled_tests }}
|
||||
IPA_VERBOSITY: "-vvv"
|
||||
|
||||
- task: PublishTestResults@2
|
||||
inputs:
|
||||
|
||||
@@ -78,6 +78,7 @@ jobs:
|
||||
IPA_DISABLED_TESTS: ${{ variables.ipa_disabled_tests }}
|
||||
IPA_ENABLED_MODULES: ${{ variables.ipa_enabled_modules }}
|
||||
IPA_ENABLED_TESTS: ${{ variables.ipa_enabled_tests }}
|
||||
IPA_VERBOSITY: "-vvv"
|
||||
|
||||
- task: PublishTestResults@2
|
||||
inputs:
|
||||
|
||||
@@ -66,6 +66,7 @@ jobs:
|
||||
IPA_DISABLED_TESTS: ${{ variables.ipa_disabled_tests }}
|
||||
IPA_ENABLED_MODULES: ${{ variables.ipa_enabled_modules }}
|
||||
IPA_ENABLED_TESTS: ${{ variables.ipa_enabled_tests }}
|
||||
IPA_VERBOSITY: "-vvv"
|
||||
|
||||
- task: PublishTestResults@2
|
||||
inputs:
|
||||
|
||||
@@ -103,7 +103,7 @@
|
||||
name: ops
|
||||
dictcheck: "error"
|
||||
register: result
|
||||
failed_when: result.changed or (result.failed and "Invalid value 'error' for argument 'dictcheck" not in result.msg)
|
||||
failed_when: result.changed or (result.failed and "is not a valid boolean" not in result.msg)
|
||||
when: ipa_version is version("4.9", ">=")
|
||||
|
||||
- name: Ensure invalid values for usercheck raise proper error.
|
||||
@@ -113,7 +113,7 @@
|
||||
name: ops
|
||||
usercheck: "error"
|
||||
register: result
|
||||
failed_when: result.changed or (result.failed and "Invalid value 'error' for argument 'usercheck'" not in result.msg)
|
||||
failed_when: result.changed or (result.failed and "is not a valid boolean" not in result.msg)
|
||||
when: ipa_version is version("4.9", ">=")
|
||||
|
||||
- name: Ensure invalid values for gracelimit raise proper error.
|
||||
|
||||
@@ -8,7 +8,7 @@ ANSIBLE_COLLECTION=freeipa-ansible_freeipa
|
||||
|
||||
use_docker=$(docker -v >/dev/null 2>&1 && echo "True" || echo "False")
|
||||
|
||||
virtualenv "$VENV"
|
||||
python -m venv "$VENV"
|
||||
# shellcheck disable=SC1091
|
||||
source "$VENV"/bin/activate
|
||||
|
||||
|
||||
@@ -9,7 +9,7 @@
|
||||
ipauser:
|
||||
ipaadmin_password: SomeADMINpassword
|
||||
ipaapi_context: "{{ ipa_context | default(omit) }}"
|
||||
name: manager1,manager2,manager3,pinky,pinky2
|
||||
name: manager1,manager2,manager3,pinky,pinky2,igagarin
|
||||
state: absent
|
||||
|
||||
- name: User manager1 present
|
||||
@@ -59,6 +59,7 @@
|
||||
#password: foo2
|
||||
principal: pa
|
||||
random: yes
|
||||
street: PinkyStreet
|
||||
city: PinkyCity
|
||||
userstate: PinkyState
|
||||
postalcode: PinkyZip
|
||||
@@ -86,6 +87,33 @@
|
||||
register: result
|
||||
failed_when: not result.changed or result.failed
|
||||
|
||||
- name: Set street, again
|
||||
ipauser:
|
||||
ipaadmin_password: SomeADMINpassword
|
||||
ipaapi_context: "{{ ipa_context | default(omit) }}"
|
||||
name: pinky
|
||||
street: PinkyStreet
|
||||
register: result
|
||||
failed_when: result.changed or result.failed
|
||||
|
||||
- name: Clear street attribute.
|
||||
ipauser:
|
||||
ipaadmin_password: SomeADMINpassword
|
||||
ipaapi_context: "{{ ipa_context | default(omit) }}"
|
||||
name: pinky
|
||||
street: ""
|
||||
register: result
|
||||
failed_when: not result.changed or result.failed
|
||||
|
||||
- name: Clear street attribute, again
|
||||
ipauser:
|
||||
ipaadmin_password: SomeADMINpassword
|
||||
ipaapi_context: "{{ ipa_context | default(omit) }}"
|
||||
name: pinky
|
||||
street: ""
|
||||
register: result
|
||||
failed_when: result.changed or result.failed
|
||||
|
||||
- name: User pinky present with changed settings
|
||||
ipauser:
|
||||
ipaadmin_password: SomeADMINpassword
|
||||
@@ -342,9 +370,107 @@
|
||||
register: result
|
||||
failed_when: result.changed or result.failed
|
||||
|
||||
- name: Ensure user with GECOS information exists.
|
||||
ipauser:
|
||||
ipaadmin_password: SomeADMINpassword
|
||||
ipaapi_context: "{{ ipa_context | default(omit) }}"
|
||||
name: igagarin
|
||||
first: Iuri
|
||||
last: Gagarin
|
||||
gecos: Юрий Алексеевич Гагарин
|
||||
register: result
|
||||
failed_when: not result.changed or result.failed
|
||||
|
||||
- name: Ensure user with GECOS information exists, again.
|
||||
ipauser:
|
||||
ipaadmin_password: SomeADMINpassword
|
||||
ipaapi_context: "{{ ipa_context | default(omit) }}"
|
||||
name: igagarin
|
||||
first: Iuri
|
||||
last: Gagarin
|
||||
gecos: Юрий Алексеевич Гагарин
|
||||
register: result
|
||||
failed_when: result.changed or result.failed
|
||||
|
||||
- name: Modify GECOS information.
|
||||
ipauser:
|
||||
ipaadmin_password: SomeADMINpassword
|
||||
ipaapi_context: "{{ ipa_context | default(omit) }}"
|
||||
name: igagarin
|
||||
gecos: Юрий Гагарин
|
||||
register: result
|
||||
failed_when: not result.changed or result.failed
|
||||
|
||||
- name: Updating with existent data, should not change user.
|
||||
ipauser:
|
||||
ipaadmin_password: SomeADMINpassword
|
||||
ipaapi_context: "{{ ipa_context | default(omit) }}"
|
||||
name: igagarin
|
||||
first: Iuri
|
||||
last: Gagarin
|
||||
gecos: Юрий Гагарин
|
||||
register: result
|
||||
failed_when: result.changed or result.failed
|
||||
|
||||
- name: Ensure GECOS parameter is cleared.
|
||||
ipauser:
|
||||
ipaadmin_password: SomeADMINpassword
|
||||
ipaapi_context: "{{ ipa_context | default(omit) }}"
|
||||
name: igagarin
|
||||
gecos: ""
|
||||
register: result
|
||||
failed_when: not result.changed or result.failed
|
||||
|
||||
- name: Ensure GECOS parameter is cleared, again
|
||||
ipauser:
|
||||
ipaadmin_password: SomeADMINpassword
|
||||
ipaapi_context: "{{ ipa_context | default(omit) }}"
|
||||
name: igagarin
|
||||
gecos: ""
|
||||
register: result
|
||||
failed_when: result.changed or result.failed
|
||||
|
||||
- name: Ensure GECOS parameter cannot be used with state absent.
|
||||
ipauser:
|
||||
ipaadmin_password: SomeADMINpassword
|
||||
ipaapi_context: "{{ ipa_context | default(omit) }}"
|
||||
name: igagarin
|
||||
gecos: Юрий Гагарин
|
||||
state: absent
|
||||
register: result
|
||||
failed_when: not result.failed or "Argument 'gecos' can not be used with action 'user' and state 'absent'" not in result.msg
|
||||
|
||||
- name: Ensure user igagarin is absent.
|
||||
ipauser:
|
||||
ipaadmin_password: SomeADMINpassword
|
||||
ipaapi_context: "{{ ipa_context | default(omit) }}"
|
||||
name: igagarin
|
||||
state: absent
|
||||
register: result
|
||||
failed_when: not result.changed or result.failed
|
||||
|
||||
- name: Ensure user with non-ascii name exists.
|
||||
ipauser:
|
||||
ipaadmin_password: SomeADMINpassword
|
||||
ipaapi_context: "{{ ipa_context | default(omit) }}"
|
||||
name: igagarin
|
||||
first: Юрий
|
||||
last: Гагарин
|
||||
register: result
|
||||
failed_when: not result.changed or result.failed
|
||||
|
||||
- name: Ensure user with non-ascii name exists has proper GECOS value.
|
||||
ipauser:
|
||||
ipaadmin_password: SomeADMINpassword
|
||||
ipaapi_context: "{{ ipa_context | default(omit) }}"
|
||||
name: igagarin
|
||||
gecos: Юрий Гагарин
|
||||
register: result
|
||||
failed_when: result.changed or result.failed
|
||||
|
||||
- name: Remove test users
|
||||
ipauser:
|
||||
ipaadmin_password: SomeADMINpassword
|
||||
ipaapi_context: "{{ ipa_context | default(omit) }}"
|
||||
name: manager1,manager2,manager3,pinky,pinky2
|
||||
name: manager1,manager2,manager3,pinky,pinky2,igagarin
|
||||
state: absent
|
||||
|
||||
107
tests/user/test_user_idp_attrs.yml
Normal file
107
tests/user/test_user_idp_attrs.yml
Normal file
@@ -0,0 +1,107 @@
|
||||
---
|
||||
- name: Test user
|
||||
hosts: "{{ ipa_test_host | default('ipaserver') }}"
|
||||
become: false
|
||||
gather_facts: false
|
||||
module_defaults:
|
||||
ipauser:
|
||||
ipaadmin_password: SomeADMINpassword
|
||||
ipaapi_context: "{{ ipa_context | default(omit) }}"
|
||||
|
||||
tasks:
|
||||
- name: Include tasks ../env_freeipa_facts.yml
|
||||
ansible.builtin.include_tasks: ../env_freeipa_facts.yml
|
||||
|
||||
# CLEANUP TEST ITEMS
|
||||
|
||||
- name: Ensure user idpuser is absent
|
||||
ipauser:
|
||||
name: idpuser
|
||||
state: absent
|
||||
|
||||
# CREATE TEST ITEMS
|
||||
- name: Run tests if FreeIPA 4.10.0+ is installed
|
||||
when: ipa_version is version('4.10.0', '>=')
|
||||
block:
|
||||
- name: Ensure IDP provider is present
|
||||
# TODO: Use an ansible-freeipa plugin instead of 'shell'
|
||||
ansible.builtin.shell:
|
||||
cmd: |
|
||||
kinit -c test_krb5_cache admin <<< SomeADMINpassword
|
||||
KRB5CCNAME=test_krb5_cache ipa idp-add keycloak --provider keycloak \
|
||||
--org master \
|
||||
--base-url https://client.ipademo.local:8443/auth \
|
||||
--client-id ipa_oidc_client \
|
||||
--secret <<< $(echo -e "Secret123\nSecret123")
|
||||
kdestroy -c test_krb5_cache -q -A
|
||||
register: addidp
|
||||
failed_when:
|
||||
- '"Added Identity Provider" not in addidp.stdout'
|
||||
- '"already exists" not in addidp.stderr'
|
||||
|
||||
# TESTS
|
||||
|
||||
- name: Ensure user idpuser is present
|
||||
ipauser:
|
||||
name: idpuser
|
||||
first: IDP
|
||||
last: User
|
||||
userauthtype: idp
|
||||
idp: keycloak
|
||||
idp_user_id: "idpuser@ipademo.local"
|
||||
register: result
|
||||
failed_when: not result.changed or result.failed
|
||||
|
||||
- name: Ensure user idpuser is present again
|
||||
ipauser:
|
||||
name: idpuser
|
||||
first: IDP
|
||||
last: User
|
||||
userauthtype: idp
|
||||
idp: keycloak
|
||||
idp_user_id: "idpuser@ipademo.local"
|
||||
register: result
|
||||
failed_when: result.changed or result.failed
|
||||
|
||||
- name: Clear 'idp_user_id'
|
||||
ipauser:
|
||||
name: idpuser
|
||||
idp_user_id: ""
|
||||
register: result
|
||||
failed_when: not result.changed or result.failed
|
||||
|
||||
- name: Clear 'idp'
|
||||
ipauser:
|
||||
name: idpuser
|
||||
idp: ""
|
||||
register: result
|
||||
failed_when: not result.changed or result.failed
|
||||
|
||||
- name: Ensure user idpuser is absent
|
||||
ipauser:
|
||||
name: idpuser
|
||||
state: absent
|
||||
register: result
|
||||
failed_when: not result.changed or result.failed
|
||||
|
||||
- name: Ensure user idpuser is absent again
|
||||
ipauser:
|
||||
name: idpuser
|
||||
state: absent
|
||||
register: result
|
||||
failed_when: result.changed or result.failed
|
||||
|
||||
|
||||
# CLEANUP TEST ITEMS
|
||||
- name: Ensure IDP provider is absent
|
||||
# TODO: Use an ansible-freeipa plugin instead of 'shell'
|
||||
ansible.builtin.shell:
|
||||
cmd: |
|
||||
kinit -c test_krb5_cache admin <<< SomeADMINpassword
|
||||
ipa idp-del keycloak
|
||||
kdestroy -c test_krb5_cache -q -A
|
||||
always:
|
||||
- name: Ensure user idpuser is absent
|
||||
ipauser:
|
||||
name: idpuser
|
||||
state: absent
|
||||
253
tests/user/test_user_smb_attrs.yml
Normal file
253
tests/user/test_user_smb_attrs.yml
Normal file
@@ -0,0 +1,253 @@
|
||||
---
|
||||
- name: Test users
|
||||
hosts: "{{ ipa_test_host | default('ipaserver') }}"
|
||||
become: no
|
||||
gather_facts: no
|
||||
|
||||
tasks:
|
||||
- name: Set FreeIPA environment facts.
|
||||
ansible.builtin.include_tasks: ../env_freeipa_facts.yml
|
||||
|
||||
- name: Only run tests for IPA 4.8.0+
|
||||
when: ipa_version is version('4.8.0', '>=')
|
||||
block:
|
||||
# SETUP
|
||||
- name: Remove test users
|
||||
ipauser:
|
||||
ipaadmin_password: SomeADMINpassword
|
||||
ipaapi_context: "{{ ipa_context | default(omit) }}"
|
||||
name: testuser
|
||||
state: absent
|
||||
|
||||
# TESTS
|
||||
- name: Ensure user testuser exists with all smb paramters
|
||||
ipauser:
|
||||
ipaadmin_password: SomeADMINpassword
|
||||
ipaapi_context: "{{ ipa_context | default(omit) }}"
|
||||
name: testuser
|
||||
first: test
|
||||
last: user
|
||||
smb_profile_path: "/some/profile/path"
|
||||
smb_home_dir: "/some/home/dir"
|
||||
smb_home_drive: "U{{ ':' }}"
|
||||
smb_logon_script: "/some/profile/script.sh"
|
||||
register: result
|
||||
failed_when: not result.changed or result.failed
|
||||
|
||||
- name: Ensure user testuser exists all smb paramters, again
|
||||
ipauser:
|
||||
ipaadmin_password: SomeADMINpassword
|
||||
ipaapi_context: "{{ ipa_context | default(omit) }}"
|
||||
name: testuser
|
||||
first: test
|
||||
last: user
|
||||
smb_logon_script: "/some/profile/script.sh"
|
||||
smb_profile_path: "/some/profile/path"
|
||||
smb_home_dir: "/some/home/dir"
|
||||
smb_home_drive: "U{{ ':' }}"
|
||||
register: result
|
||||
failed_when: result.changed or result.failed
|
||||
|
||||
- name: Check SMB logon script is correct
|
||||
ipauser:
|
||||
ipaadmin_password: SomeADMINpassword
|
||||
ipaapi_context: "{{ ipa_context | default(omit) }}"
|
||||
name: testuser
|
||||
smb_logon_script: "/some/profile/script.sh"
|
||||
register: result
|
||||
check_mode: true
|
||||
failed_when: result.changed or result.failed
|
||||
|
||||
- name: Check SMB profile path is correct
|
||||
ipauser:
|
||||
ipaadmin_password: SomeADMINpassword
|
||||
ipaapi_context: "{{ ipa_context | default(omit) }}"
|
||||
name: testuser
|
||||
smb_profile_path: "/some/profile/path"
|
||||
register: result
|
||||
check_mode: true
|
||||
failed_when: result.changed or result.failed
|
||||
|
||||
- name: Check SMB Home Directory is correct
|
||||
ipauser:
|
||||
ipaadmin_password: SomeADMINpassword
|
||||
ipaapi_context: "{{ ipa_context | default(omit) }}"
|
||||
name: testuser
|
||||
smb_home_dir: "/some/home/dir"
|
||||
register: result
|
||||
check_mode: true
|
||||
failed_when: result.changed or result.failed
|
||||
|
||||
- name: Check SMB Home Drive is correct
|
||||
ipauser:
|
||||
ipaadmin_password: SomeADMINpassword
|
||||
ipaapi_context: "{{ ipa_context | default(omit) }}"
|
||||
name: testuser
|
||||
first: test
|
||||
last: user
|
||||
smb_home_drive: "U{{ ':' }}"
|
||||
register: result
|
||||
check_mode: true
|
||||
failed_when: result.changed or result.failed
|
||||
|
||||
- name: Set SMB logon script
|
||||
ipauser:
|
||||
ipaadmin_password: SomeADMINpassword
|
||||
ipaapi_context: "{{ ipa_context | default(omit) }}"
|
||||
name: testuser
|
||||
smb_logon_script: "/some/profile/another_script.sh"
|
||||
register: result
|
||||
failed_when: not result.changed or result.failed
|
||||
|
||||
- name: Set SMB logon script, again
|
||||
ipauser:
|
||||
ipaadmin_password: SomeADMINpassword
|
||||
ipaapi_context: "{{ ipa_context | default(omit) }}"
|
||||
name: testuser
|
||||
smb_logon_script: "/some/profile/another_script.sh"
|
||||
register: result
|
||||
failed_when: result.changed or result.failed
|
||||
|
||||
- name: Clear SMB logon script
|
||||
ipauser:
|
||||
ipaadmin_password: SomeADMINpassword
|
||||
ipaapi_context: "{{ ipa_context | default(omit) }}"
|
||||
name: testuser
|
||||
smb_logon_script: ""
|
||||
register: result
|
||||
failed_when: not result.changed or result.failed
|
||||
|
||||
- name: Clear SMB logon script, again
|
||||
ipauser:
|
||||
ipaadmin_password: SomeADMINpassword
|
||||
ipaapi_context: "{{ ipa_context | default(omit) }}"
|
||||
name: testuser
|
||||
smb_logon_script: ""
|
||||
register: result
|
||||
failed_when: result.changed or result.failed
|
||||
|
||||
- name: Set SMB profile path
|
||||
ipauser:
|
||||
ipaadmin_password: SomeADMINpassword
|
||||
ipaapi_context: "{{ ipa_context | default(omit) }}"
|
||||
name: testuser
|
||||
smb_profile_path: "/some/profile/another_path"
|
||||
register: result
|
||||
failed_when: not result.changed or result.failed
|
||||
|
||||
- name: Set SMB profile path, again
|
||||
ipauser:
|
||||
ipaadmin_password: SomeADMINpassword
|
||||
ipaapi_context: "{{ ipa_context | default(omit) }}"
|
||||
name: testuser
|
||||
smb_profile_path: "/some/profile/another_path"
|
||||
register: result
|
||||
failed_when: result.changed or result.failed
|
||||
|
||||
- name: Clear SMB profile path
|
||||
ipauser:
|
||||
ipaadmin_password: SomeADMINpassword
|
||||
ipaapi_context: "{{ ipa_context | default(omit) }}"
|
||||
name: testuser
|
||||
smb_profile_path: ""
|
||||
register: result
|
||||
failed_when: not result.changed or result.failed
|
||||
|
||||
- name: Clear SMB profile, again
|
||||
ipauser:
|
||||
ipaadmin_password: SomeADMINpassword
|
||||
ipaapi_context: "{{ ipa_context | default(omit) }}"
|
||||
name: testuser
|
||||
smb_profile_path: ""
|
||||
register: result
|
||||
failed_when: result.changed or result.failed
|
||||
|
||||
- name: Set SMB home directory
|
||||
ipauser:
|
||||
ipaadmin_password: SomeADMINpassword
|
||||
ipaapi_context: "{{ ipa_context | default(omit) }}"
|
||||
name: testuser
|
||||
smb_home_dir: "/some/other/home"
|
||||
register: result
|
||||
failed_when: not result.changed or result.failed
|
||||
|
||||
- name: Set SMB home directory, again
|
||||
ipauser:
|
||||
ipaadmin_password: SomeADMINpassword
|
||||
ipaapi_context: "{{ ipa_context | default(omit) }}"
|
||||
name: testuser
|
||||
smb_home_dir: "/some/other/home"
|
||||
register: result
|
||||
failed_when: result.changed or result.failed
|
||||
|
||||
- name: Clear SMB home directory
|
||||
ipauser:
|
||||
ipaadmin_password: SomeADMINpassword
|
||||
ipaapi_context: "{{ ipa_context | default(omit) }}"
|
||||
name: testuser
|
||||
smb_home_dir: ""
|
||||
register: result
|
||||
failed_when: not result.changed or result.failed
|
||||
|
||||
- name: Clear SMB home directory, again
|
||||
ipauser:
|
||||
ipaadmin_password: SomeADMINpassword
|
||||
ipaapi_context: "{{ ipa_context | default(omit) }}"
|
||||
name: testuser
|
||||
smb_home_dir: ""
|
||||
register: result
|
||||
failed_when: result.changed or result.failed
|
||||
|
||||
- name: Set SMB home drive
|
||||
ipauser:
|
||||
ipaadmin_password: SomeADMINpassword
|
||||
ipaapi_context: "{{ ipa_context | default(omit) }}"
|
||||
name: testuser
|
||||
smb_home_drive: "Z{{ ':' }}"
|
||||
register: result
|
||||
failed_when: not result.changed or result.failed
|
||||
|
||||
- name: Set SMB home drive, again
|
||||
ipauser:
|
||||
ipaadmin_password: SomeADMINpassword
|
||||
ipaapi_context: "{{ ipa_context | default(omit) }}"
|
||||
name: testuser
|
||||
smb_home_drive: "Z{{ ':' }}"
|
||||
register: result
|
||||
failed_when: result.changed or result.failed
|
||||
|
||||
- name: Set SMB home drive to invalid value
|
||||
ipauser:
|
||||
ipaadmin_password: SomeADMINpassword
|
||||
ipaapi_context: "{{ ipa_context | default(omit) }}"
|
||||
name: testuser
|
||||
smb_home_drive: "INVALID:"
|
||||
register: result
|
||||
failed_when: not result.failed or "value of smb_home_drive must be one of" not in result.msg
|
||||
|
||||
- name: Clear SMB home drive
|
||||
ipauser:
|
||||
ipaadmin_password: SomeADMINpassword
|
||||
ipaapi_context: "{{ ipa_context | default(omit) }}"
|
||||
name: testuser
|
||||
smb_home_drive: ""
|
||||
register: result
|
||||
failed_when: not result.changed or result.failed
|
||||
|
||||
- name: Clear SMB home drive, again
|
||||
ipauser:
|
||||
ipaadmin_password: SomeADMINpassword
|
||||
ipaapi_context: "{{ ipa_context | default(omit) }}"
|
||||
name: testuser
|
||||
smb_home_drive: ""
|
||||
register: result
|
||||
failed_when: result.changed or result.failed
|
||||
|
||||
always:
|
||||
# CLEANUP
|
||||
- name: Remove test users
|
||||
ipauser:
|
||||
ipaadmin_password: SomeADMINpassword
|
||||
ipaapi_context: "{{ ipa_context | default(omit) }}"
|
||||
name: testuser
|
||||
state: absent
|
||||
@@ -151,6 +151,7 @@
|
||||
#password: foo2
|
||||
principal: pa
|
||||
random: yes
|
||||
street: PinkyStreet
|
||||
city: PinkyCity
|
||||
userstate: PinkyState
|
||||
postalcode: PinkyZip
|
||||
@@ -194,6 +195,7 @@
|
||||
#password: foo2
|
||||
principal: pa
|
||||
random: yes
|
||||
street: PinkyStreet
|
||||
city: PinkyCity
|
||||
userstate: PinkyState
|
||||
postalcode: PinkyZip
|
||||
|
||||
@@ -162,7 +162,7 @@ list_images() {
|
||||
|
||||
# Defaults
|
||||
|
||||
ANSIBLE_VERSION=${ANSIBLE_VERSION:-'ansible-core>=2.12,<2.13'}
|
||||
ANSIBLE_VERSION=${ANSIBLE_VERSION:-'ansible-core'}
|
||||
verbose=""
|
||||
FORCE_ENV="N"
|
||||
CONTINUE_ON_ERROR=""
|
||||
@@ -259,17 +259,18 @@ then
|
||||
log info "Installing Ansible: ${ANSIBLE_VERSION}"
|
||||
pip install --quiet "${ANSIBLE_VERSION}"
|
||||
log debug "Ansible version: $(ansible --version | sed -n "1p")${RST}"
|
||||
if [ -n "${ANSIBLE_COLLECTIONS}" ]
|
||||
then
|
||||
log warn "Installed collections will not be removed after execution."
|
||||
log none "Installing: Ansible Collection ${ANSIBLE_COLLECTIONS}"
|
||||
# shellcheck disable=SC2086
|
||||
quiet ansible-galaxy collection install ${ANSIBLE_COLLECTIONS} || die "Failed to install Ansible collections."
|
||||
fi
|
||||
else
|
||||
log info "Using current virtual environment."
|
||||
fi
|
||||
|
||||
if [ -n "${ANSIBLE_COLLECTIONS}" ]
|
||||
then
|
||||
log warn "Installed collections will not be removed after execution."
|
||||
log none "Installing: Ansible Collection ${ANSIBLE_COLLECTIONS}"
|
||||
# shellcheck disable=SC2086
|
||||
quiet ansible-galaxy collection install ${ANSIBLE_COLLECTIONS} || die "Failed to install Ansible collections."
|
||||
fi
|
||||
|
||||
# Ansible configuration
|
||||
export ANSIBLE_ROLES_PATH="${TOPDIR}/roles"
|
||||
export ANSIBLE_LIBRARY="${TOPDIR}/plugins:${TOPDIR}/molecule"
|
||||
|
||||
Reference in New Issue
Block a user