Skip to content
Snippets Groups Projects
Unverified Commit 411d363d authored by Thomas Woerner's avatar Thomas Woerner Committed by GitHub
Browse files

Merge pull request #1056 from rjeffman/ipauser_smb_params

ipauser: Add support for SMB attributes.
parents dab64c7c 57ad57dd
No related branches found
No related tags found
No related merge requests found
...@@ -353,6 +353,33 @@ Example playbook to ensure users are absent: ...@@ -353,6 +353,33 @@ Example playbook to ensure users are absent:
state: 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 Variables
========= =========
...@@ -425,6 +452,10 @@ Variable | Description | Required ...@@ -425,6 +452,10 @@ Variable | Description | Required
  | `subject` - Subject of the certificate, only usable together with `issuer` option. | no   | `subject` - Subject of the certificate, only usable together with `issuer` option. | no
  | `data` - Certmap data, not usable with other certmapdata options. | no   | `data` - Certmap data, not usable with other certmapdata options. | no
`noprivate` | Do not create user private group. (bool) | 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 `nomembers` | Suppress processing of membership attributes. (bool) | no
......
---
- 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:"
...@@ -242,6 +242,31 @@ options: ...@@ -242,6 +242,31 @@ options:
description: Employee Type description: Employee Type
type: str type: str
required: false 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: preferredlanguage:
description: Preferred Language description: Preferred Language
type: str type: str
...@@ -474,6 +499,31 @@ options: ...@@ -474,6 +499,31 @@ options:
description: Employee Type description: Employee Type
type: str type: str
required: false 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: preferredlanguage:
description: Preferred Language description: Preferred Language
type: str type: str
...@@ -613,6 +663,17 @@ EXAMPLES = """ ...@@ -613,6 +663,17 @@ EXAMPLES = """
ipaadmin_password: SomeADMINpassword ipaadmin_password: SomeADMINpassword
name: pinky,brain name: pinky,brain
state: disabled 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 = """ RETURN = """
...@@ -673,7 +734,8 @@ def gen_args(first, last, fullname, displayname, initials, homedir, gecos, ...@@ -673,7 +734,8 @@ def gen_args(first, last, fullname, displayname, initials, homedir, gecos,
random, uid, gid, street, city, userstate, postalcode, phone, random, uid, gid, street, city, userstate, postalcode, phone,
mobile, pager, fax, orgunit, title, carlicense, sshpubkey, mobile, pager, fax, orgunit, title, carlicense, sshpubkey,
userauthtype, userclass, radius, radiususer, departmentnumber, userauthtype, userclass, radius, radiususer, departmentnumber,
employeenumber, employeetype, preferredlanguage, noprivate, employeenumber, employeetype, preferredlanguage, smb_logon_script,
smb_profile_path, smb_home_dir, smb_home_drive, noprivate,
nomembers): nomembers):
# principal, manager, certificate and certmapdata are handled not in here # principal, manager, certificate and certmapdata are handled not in here
_args = {} _args = {}
...@@ -751,6 +813,14 @@ def gen_args(first, last, fullname, displayname, initials, homedir, gecos, ...@@ -751,6 +813,14 @@ def gen_args(first, last, fullname, displayname, initials, homedir, gecos,
_args["noprivate"] = noprivate _args["noprivate"] = noprivate
if nomembers is not None: if nomembers is not None:
_args["no_members"] = nomembers _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 return _args
...@@ -761,7 +831,9 @@ def check_parameters( # pylint: disable=unused-argument ...@@ -761,7 +831,9 @@ def check_parameters( # pylint: disable=unused-argument
mobile, pager, fax, orgunit, title, manager, carlicense, sshpubkey, mobile, pager, fax, orgunit, title, manager, carlicense, sshpubkey,
userauthtype, userclass, radius, radiususer, departmentnumber, userauthtype, userclass, radius, radiususer, departmentnumber,
employeenumber, employeetype, preferredlanguage, certificate, employeenumber, employeetype, preferredlanguage, certificate,
certmapdata, noprivate, nomembers, preserve, update_password): certmapdata, noprivate, nomembers, preserve, update_password,
smb_logon_script, smb_profile_path, smb_home_dir, smb_home_drive,
):
if state == "present" and action == "user": if state == "present" and action == "user":
invalid = ["preserve"] invalid = ["preserve"]
else: else:
...@@ -773,7 +845,8 @@ def check_parameters( # pylint: disable=unused-argument ...@@ -773,7 +845,8 @@ def check_parameters( # pylint: disable=unused-argument
"sshpubkey", "userauthtype", "userclass", "radius", "radiususer", "sshpubkey", "userauthtype", "userclass", "radius", "radiususer",
"departmentnumber", "employeenumber", "employeetype", "departmentnumber", "employeenumber", "employeetype",
"preferredlanguage", "noprivate", "nomembers", "update_password", "preferredlanguage", "noprivate", "nomembers", "update_password",
"gecos", "gecos", "smb_logon_script", "smb_profile_path", "smb_home_dir",
"smb_home_drive",
] ]
if state == "present" and action == "member": if state == "present" and action == "member":
...@@ -961,6 +1034,17 @@ def main(): ...@@ -961,6 +1034,17 @@ def main():
departmentnumber=dict(type="list", elements="str", default=None), departmentnumber=dict(type="list", elements="str", default=None),
employeenumber=dict(type="str", default=None), employeenumber=dict(type="str", default=None),
employeetype=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), preferredlanguage=dict(type="str", default=None),
certificate=dict(type="list", elements="str", certificate=dict(type="list", elements="str",
aliases=["usercertificate"], default=None), aliases=["usercertificate"], default=None),
...@@ -1073,6 +1157,10 @@ def main(): ...@@ -1073,6 +1157,10 @@ def main():
employeenumber = ansible_module.params_get("employeenumber") employeenumber = ansible_module.params_get("employeenumber")
employeetype = ansible_module.params_get("employeetype") employeetype = ansible_module.params_get("employeetype")
preferredlanguage = ansible_module.params_get("preferredlanguage") 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")
certificate = ansible_module.params_get("certificate") certificate = ansible_module.params_get("certificate")
certmapdata = ansible_module.params_get("certmapdata") certmapdata = ansible_module.params_get("certmapdata")
noprivate = ansible_module.params_get("noprivate") noprivate = ansible_module.params_get("noprivate")
...@@ -1105,7 +1193,8 @@ def main(): ...@@ -1105,7 +1193,8 @@ def main():
manager, carlicense, sshpubkey, userauthtype, userclass, radius, manager, carlicense, sshpubkey, userauthtype, userclass, radius,
radiususer, departmentnumber, employeenumber, employeetype, radiususer, departmentnumber, employeenumber, employeetype,
preferredlanguage, certificate, certmapdata, noprivate, nomembers, preferredlanguage, certificate, certmapdata, noprivate, nomembers,
preserve, update_password) preserve, update_password, smb_logon_script, smb_profile_path,
smb_home_dir, smb_home_drive)
certmapdata = convert_certmapdata(certmapdata) certmapdata = convert_certmapdata(certmapdata)
# Use users if names is None # Use users if names is None
...@@ -1191,6 +1280,10 @@ def main(): ...@@ -1191,6 +1280,10 @@ def main():
employeenumber = user.get("employeenumber") employeenumber = user.get("employeenumber")
employeetype = user.get("employeetype") employeetype = user.get("employeetype")
preferredlanguage = user.get("preferredlanguage") 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")
certificate = user.get("certificate") certificate = user.get("certificate")
certmapdata = user.get("certmapdata") certmapdata = user.get("certmapdata")
noprivate = user.get("noprivate") noprivate = user.get("noprivate")
...@@ -1206,7 +1299,8 @@ def main(): ...@@ -1206,7 +1299,8 @@ def main():
radiususer, departmentnumber, employeenumber, radiususer, departmentnumber, employeenumber,
employeetype, preferredlanguage, certificate, employeetype, preferredlanguage, certificate,
certmapdata, noprivate, nomembers, preserve, certmapdata, noprivate, nomembers, preserve,
update_password) update_password, smb_logon_script, smb_profile_path,
smb_home_dir, smb_home_drive)
certmapdata = convert_certmapdata(certmapdata) certmapdata = convert_certmapdata(certmapdata)
# Extend email addresses # Extend email addresses
...@@ -1248,6 +1342,21 @@ def main(): ...@@ -1248,6 +1342,21 @@ def main():
msg="The use of certmapdata is not supported by " msg="The use of certmapdata is not supported by "
"your IPA version") "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")
# Make sure user exists # Make sure user exists
res_find = find_user(ansible_module, name) res_find = find_user(ansible_module, name)
...@@ -1262,7 +1371,8 @@ def main(): ...@@ -1262,7 +1371,8 @@ def main():
postalcode, phone, mobile, pager, fax, orgunit, title, postalcode, phone, mobile, pager, fax, orgunit, title,
carlicense, sshpubkey, userauthtype, userclass, radius, carlicense, sshpubkey, userauthtype, userclass, radius,
radiususer, departmentnumber, employeenumber, employeetype, radiususer, departmentnumber, employeenumber, employeetype,
preferredlanguage, noprivate, nomembers) preferredlanguage, smb_logon_script, smb_profile_path,
smb_home_dir, smb_home_drive, noprivate, nomembers)
if action == "user": if action == "user":
# Found the user # Found the user
...@@ -1298,8 +1408,21 @@ def main(): ...@@ -1298,8 +1408,21 @@ def main():
ansible_module.fail_json( ansible_module.fail_json(
msg="Last name is needed") 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]) commands.append([name, "user_add", args])
if smb_attrs:
commands.append([name, "user_mod", smb_attrs])
# Handle members: principal, manager, certificate and # Handle members: principal, manager, certificate and
# certmapdata # certmapdata
if res_find is not None: if res_find is not None:
......
---
- 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
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Please to comment