Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Bring win_lgpo changes in from master #55431

Closed
wants to merge 13 commits into from
1,261 changes: 1,012 additions & 249 deletions salt/modules/win_lgpo.py

Large diffs are not rendered by default.

225 changes: 122 additions & 103 deletions salt/states/win_lgpo.py
Original file line number Diff line number Diff line change
Expand Up @@ -7,9 +7,11 @@

This state allows configuring local Windows Group Policy

The state can be used to ensure the setting of a single policy or multiple policies in one pass.
The state can be used to ensure the setting of a single policy or multiple
policies in one pass.

Single policies must specify the policy name, the setting, and the policy class (Machine/User/Both)
Single policies must specify the policy name, the setting, and the policy class
(Machine/User/Both)

Example single policy configuration

Expand All @@ -24,7 +26,7 @@
.. code-block:: yaml

Account lockout duration:
gpo.set:
lgpo.set:
- setting: 120
- policy_class: Machine

Expand All @@ -35,10 +37,11 @@
Company Local Group Policy:
lgpo.set:
- computer_policy:
Deny logon locally: Guest
Deny log on locally:
- Guest
Account lockout duration: 120
Account lockout threshold: 10
Reset account lockout counter after: 1440
Reset account lockout counter after: 120
Enforce password history: 24
Maximum password age: 60
Minimum password age: 1
Expand All @@ -63,9 +66,9 @@
Maximum password age: 60
Minimum password age: 1
Minimum password length: 14
Account lockout duration: 1440
Account lockout duration: 120
Account lockout threshold: 10
Reset account lockout counter after: 1440
Reset account lockout counter after: 120
Manage auditing and security log:
- "BUILTIN\\Administrators"
Replace a process level token:
Expand Down Expand Up @@ -100,9 +103,7 @@
"Set the intranet update service for detecting updates": http://mywsus
"Set the intranet statistics server": http://mywsus
- cumulative_rights_assignments: True

'''

# Import python libs
from __future__ import absolute_import, unicode_literals, print_function
import logging
Expand All @@ -111,6 +112,7 @@
import salt.utils.data
import salt.utils.dictdiffer
import salt.utils.json
import salt.utils.win_functions

# Import 3rd party libs
from salt.ext import six
Expand All @@ -133,7 +135,7 @@ def _compare_policies(new_policy, current_policy):
otherwise ``False``
'''
# Compared dicts, lists, and strings
if isinstance(new_policy, six.string_types):
if isinstance(new_policy, (six.string_types, six.integer_types)):
return new_policy == current_policy
elif isinstance(new_policy, list):
if isinstance(current_policy, list):
Expand All @@ -157,54 +159,64 @@ def set_(name,
cumulative_rights_assignments=True,
adml_language='en-US'):
'''
Ensure the specified policy is set

name
the name of a single policy to configure

setting
the configuration setting for the single named policy
if this argument is used the computer_policy/user_policy arguments will be ignored

policy_class
the policy class of the single named policy to configure
this can "machine", "user", or "both"

computer_policy
a dict of policyname: value pairs of a set of computer policies to configure
if this argument is used, the name/setting/policy_class arguments will be ignored

user_policy
a dict of policyname: value pairs of a set of user policies to configure
if this argument is used, the name/setting/policy_class arguments will be ignored

cumulative_rights_assignments
determine if any user right assignment policies specified will be cumulative
or explicit

adml_language
the adml language to use for AMDX policy data/display conversions
Ensure the specified policy is set.

.. warning::
The ``setting`` argument cannot be used in conjunction with the
``computer_policy`` or ``user_policy`` arguments

Args:
name (str): The name of a single policy to configure

setting (str, dict, list):
The configuration setting for the single named policy. If this
argument is used the ``computer_policy`` / ``user_policy`` arguments
will be ignored

policy_class (str):
The policy class of the single named policy to configure. This can
``machine``, ``user``, or ``both``

computer_policy (dict):
A dictionary of containing the policy name and key/value pairs of a
set of computer policies to configure. If this argument is used, the
``name`` / ``policy_class`` arguments will be ignored

user_policy (dict):
A dictionary of containing the policy name and key/value pairs of a
set of user policies to configure. If this argument is used, the
``name`` / ``policy_class`` arguments will be ignored

cumulative_rights_assignments (bool):
If user rights assignments are being configured, determines if any
user right assignment policies specified will be cumulative or
explicit

adml_language (str):
The adml language to use for AMDX policy data/display conversions.
Default is ``en-US``
'''
ret = {'name': name,
'result': True,
'changes': {},
'comment': ''}
policy_classes = ['machine', 'computer', 'user', 'both']
if not setting and not computer_policy and not user_policy:
msg = 'At least one of the parameters setting, computer_policy, or user_policy'
msg = msg + ' must be specified.'
msg = 'At least one of the parameters setting, computer_policy, or ' \
'user_policy must be specified.'
ret['result'] = False
ret['comment'] = msg
return ret
if setting and not policy_class:
msg = 'A single policy setting was specified but the policy_class was not specified.'
msg = 'A single policy setting was specified but the policy_class ' \
'was not specified.'
ret['result'] = False
ret['comment'] = msg
return ret
if setting and (computer_policy or user_policy):
msg = 'The setting and computer_policy/user_policy parameters are mutually exclusive. Please'
msg = msg + ' specify either a policy name and setting or a computer_policy and/or user_policy'
msg = msg + ' dict'
msg = 'The setting and computer_policy/user_policy parameters are ' \
'mutually exclusive. Please specify either a policy name and ' \
'setting or a computer_policy and/or user_policy dict'
ret['result'] = False
ret['comment'] = msg
return ret
Expand Down Expand Up @@ -238,68 +250,62 @@ def set_(name,
computer_policy[name] = setting
elif policy_class.lower() == 'user':
user_policy[name] = setting
elif policy_class.lower() == 'machine' or policy_class.lower() == 'computer':
elif policy_class.lower() in ['machine', 'computer']:
computer_policy[name] = setting
pol_data = {}
pol_data['user'] = {'output_section': 'User Configuration',
'requested_policy': user_policy,
'policy_lookup': {}}
pol_data['machine'] = {'output_section': 'Computer Configuration',
'requested_policy': computer_policy,
'policy_lookup': {}}

pol_data = {
'user': {
'requested_policy': user_policy,
'policy_lookup': {}},
'machine': {
'requested_policy': computer_policy,
'policy_lookup': {}}}

current_policy = {}
for p_class, p_data in six.iteritems(pol_data):
if p_data['requested_policy']:
for policy_name, policy_setting in six.iteritems(p_data['requested_policy']):
lookup = __salt__['lgpo.get_policy_info'](policy_name,
p_class,
adml_language=adml_language)
for p_name, _ in six.iteritems(p_data['requested_policy']):
lookup = __salt__['lgpo.get_policy_info'](
policy_name=p_name,
policy_class=p_class,
adml_language=adml_language)
if lookup['policy_found']:
pol_data[p_class]['policy_lookup'][policy_name] = lookup
pol_data[p_class]['policy_lookup'][p_name] = lookup
# Since we found the policy, let's get the current setting
# as well
current_policy.setdefault(p_class, {})
current_policy[p_class][p_name] = __salt__['lgpo.get_policy'](
policy_name=p_name,
policy_class=p_class,
adml_language=adml_language,
return_value_only=True)
else:
ret['comment'] = ' '.join([ret['comment'], lookup['message']])
ret['result'] = False
if not ret['result']:
return ret

current_policy = __salt__['lgpo.get'](policy_class=policy_class,
adml_language=adml_language,
hierarchical_return=False)
log.debug('pol_data == %s', pol_data)
log.debug('current policy == %s', current_policy)

# compare policies
policy_changes = []
for policy_section, policy_data in six.iteritems(pol_data):
pol_id = None
if policy_data and policy_data['output_section'] in current_policy:
for policy_name, policy_setting in six.iteritems(policy_data['requested_policy']):
currently_set = False
# Check Case sensitive first (faster)
if policy_name in current_policy[policy_data['output_section']]:
for p_class, p_data in six.iteritems(pol_data):
requested_policy = p_data.get('requested_policy')
if requested_policy:
for p_name, p_setting in six.iteritems(requested_policy):
if p_name in current_policy[p_class]:
currently_set = True
pol_id = policy_name
# Check case insensitive
elif policy_name.lower() in (k.lower() for k in current_policy[policy_data['output_section']]):
for p_name in current_policy[policy_data['output_section']]:
if policy_name.lower() == p_name.lower():
currently_set = True
pol_id = p_name
break
# Check aliases
else:
for alias in policy_data['policy_lookup'][policy_name]['policy_aliases']:
log.debug('checking alias %s', alias)
if alias in current_policy[policy_data['output_section']]:
currently_set = True
pol_id = alias
break
if currently_set:
# compare
log.debug('need to compare %s from '
'current/requested policy', policy_name)
log.debug('need to compare %s from current/requested '
'policy', p_name)
changes = False
requested_policy_json = salt.utils.json.dumps(policy_data['requested_policy'][policy_name], sort_keys=True).lower()
current_policy_json = salt.utils.json.dumps(current_policy[policy_data['output_section']][pol_id], sort_keys=True).lower()
requested_policy_json = salt.utils.json.dumps(
p_data['requested_policy'][p_name],
sort_keys=True).lower()
current_policy_json = salt.utils.json.dumps(
current_policy[p_class][p_name],
sort_keys=True).lower()

requested_policy_check = salt.utils.json.loads(requested_policy_json)
current_policy_check = salt.utils.json.loads(current_policy_json)
Expand All @@ -310,35 +316,39 @@ def set_(name,

if not policies_are_equal:
additional_policy_comments = []
if policy_data['policy_lookup'][policy_name]['rights_assignment'] and cumulative_rights_assignments:
for user in policy_data['requested_policy'][policy_name]:
if user not in current_policy[policy_data['output_section']][pol_id]:
changes = True
if p_data['policy_lookup'][p_name]['rights_assignment'] and cumulative_rights_assignments:
for user in p_data['requested_policy'][p_name]:
if user not in current_policy[p_class][p_name]:
user = salt.utils.win_functions.get_sam_name(user)
if user not in current_policy[p_class][p_name]:
changes = True
else:
additional_policy_comments.append('"{0}" is already granted the right'.format(user))
else:
additional_policy_comments.append('"{0}" is already granted the right'.format(user))
else:
changes = True
if changes:
log.debug('%s current policy != requested policy',
policy_name)
p_name)
log.debug(
'we compared %s to %s',
requested_policy_json, current_policy_json
)
policy_changes.append(policy_name)
policy_changes.append(p_name)
else:
if additional_policy_comments:
ret['comment'] = '"{0}" is already set ({1})\n'.format(policy_name, ', '.join(additional_policy_comments))
ret['comment'] = '"{0}" is already set ({1})\n'.format(p_name, ', '.join(additional_policy_comments))
else:
ret['comment'] = '"{0}" is already set\n'.format(policy_name) + ret['comment']
ret['comment'] = '"{0}" is already set\n'.format(p_name) + ret['comment']
else:
log.debug('%s current setting matches '
'the requested setting', policy_name)
ret['comment'] = '"{0}" is already set\n'.format(policy_name) + ret['comment']
'the requested setting', p_name)
ret['comment'] = '"{0}" is already set\n'.format(p_name) + ret['comment']
else:
policy_changes.append(policy_name)
policy_changes.append(p_name)
log.debug('policy %s is not set, we will configure it',
policy_name)
p_name)
if __opts__['test']:
if policy_changes:
ret['result'] = None
Expand All @@ -354,11 +364,20 @@ def set_(name,
adml_language=adml_language)
if _ret:
ret['result'] = _ret
new_policy = {}
for p_class, p_data in six.iteritems(pol_data):
if p_data['requested_policy']:
for p_name, p_setting in six.iteritems(
p_data['requested_policy']):
new_policy.setdefault(p_class, {})
new_policy[p_class][p_name] = __salt__[
'lgpo.get_policy'](
policy_name=p_name,
policy_class=p_class,
adml_language=adml_language,
return_value_only=True)
ret['changes'] = salt.utils.dictdiffer.deep_diff(
current_policy,
__salt__['lgpo.get'](policy_class=policy_class,
adml_language=adml_language,
hierarchical_return=False))
old=current_policy, new=new_policy)
if ret['changes']:
ret['comment'] = 'The following policies changed:\n{0}' \
''.format('\n'.join(policy_changes))
Expand Down
4 changes: 3 additions & 1 deletion salt/utils/win_reg.py
Original file line number Diff line number Diff line change
Expand Up @@ -89,6 +89,8 @@ def _to_unicode(vdata):
# None does not convert to Unicode
if vdata is None:
return None
if isinstance(vdata, int):
vdata = str(vdata)
return salt.utils.stringutils.to_unicode(vdata, 'utf-8')


Expand Down Expand Up @@ -522,7 +524,7 @@ def read_value(hive, key, vname=None, use_32bit_registry=False):
try:
# RegQueryValueEx returns and accepts unicode data
vdata, vtype = win32api.RegQueryValueEx(handle, local_vname)
if vdata or vdata in [0, '']:
if vdata or vdata in [0, '', []]:
# Only convert text types to unicode
ret['vtype'] = registry.vtype_reverse[vtype]
if vtype == win32con.REG_MULTI_SZ:
Expand Down
Loading