salt/states/win_lgpo.py
# -*- coding: utf-8 -*-
'''
Manage Windows Local Group Policy
=================================
.. versionadded:: 2016.11.0
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.
Single policies must specify the policy name, the setting, and the policy class (Machine/User/Both)
Example single policy configuration
.. code-block:: yaml
Ensure Account Lockout Duration:
lgpo.set:
- name: Account lockout duration
- setting: 90
- policy_class: Machine
.. code-block:: yaml
Account lockout duration:
gpo.set:
- setting: 120
- policy_class: Machine
Multiple policy configuration
.. code-block:: yaml
Company Local Group Policy:
lgpo.set:
- computer_policy:
Deny logon locally: Guest
Account lockout duration: 120
Account lockout threshold: 10
Reset account lockout counter after: 1440
Enforce password history: 24
Maximum password age: 60
Minimum password age: 1
Minimum password length: 14
Password must meet complexity requirements: Enabled
Store passwords using reversible encryption: Disabled
Configure Automatic Updates:
Configure automatic updating: 4 - Auto download and schedule the intsall
Scheduled install day: 7 - Every Saturday
Scheduled install time: 17:00
Specify intranet Microsoft update service location:
Set the intranet update service for detecting updates: http://mywsus
Set the intranet statistics server: http://mywsus
- user_policy:
Do not process the legacy run list: Enabled
.. code-block:: text
server_policy:
lgpo.set:
- computer_policy:
Maximum password age: 60
Minimum password age: 1
Minimum password length: 14
Account lockout duration: 1440
Account lockout threshold: 10
Reset account lockout counter after: 1440
Manage auditing and security log:
- "BUILTIN\\Administrators"
Replace a process level token:
- "NT AUTHORITY\\NETWORK SERVICE"
- "NT AUTHORITY\\LOCAL SERVICE"
"Accounts: Guest account status": Disabled
"Accounts: Rename guest account": Not_4_U
"Audit: Audit the use of Backup and Restore privilege": Enabled
"Interactive logon: Do not display last user name": Enabled
"Network\\DNS Client\\Dynamic update": Disabled
"System\\Logon\\Do not display the Getting Started welcome screen at logon": Enabled
"Windows Components\\Remote Desktop Services\\Remote Desktop Session Host\\Connections\\Select RDP transport protocols":
"Select Transport Type": "Use both UDP and TCP"
"Windows Components\\Windows Update\\Allow Automatic Updates immediate installation": Enabled
"Windows Components\\Windows Update\\Allow non-administrators to receive update notifications": Disabled
"Windows Components\\Windows Update\\Always automatically restart at the scheduled time":
"The restart timer will give users this much time to save their work (minutes)": 15
"Windows Components\\Windows Update\\Automatic Updates detection frequency":
"Check for updates at the following interval (hours)": 1
"Windows Components\\Windows Update\\Configure Automatic Updates":
"Configure automatic updating": 4 - Auto download and schedule the install
"Install during automatic maintenance": False
"Scheduled install day": 7 - Every Saturday
"Scheduled install time": "17:00"
"Windows Components\\Windows Update\\Delay Restart for scheduled installations":
"Wait the following period before proceeding with a scheduled restart (minutes)": 1
"Windows Components\\Windows Update\\No auto-restart with logged on users for scheduled automatic updates installations": Disabled
"Windows Components\\Windows Update\\Re-prompt for restart with scheduled installations":
"Wait the following period before prompting again with a scheduled restart (minutes)": 30
"Windows Components\\Windows Update\\Reschedule Automatic Updates scheduled installations": Disabled
"Windows Components\\Windows Update\\Specify intranet Microsoft update service location":
"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
# Import salt libs
import salt.utils.data
import salt.utils.dictdiffer
import salt.utils.json
# Import 3rd party libs
from salt.ext import six
log = logging.getLogger(__name__)
__virtualname__ = 'lgpo'
__func_alias__ = {'set_': 'set'}
def __virtual__():
'''
load this state if the win_lgpo module exists
'''
return __virtualname__ if 'lgpo.set' in __salt__ else False
def set_(name,
setting=None,
policy_class=None,
computer_policy=None,
user_policy=None,
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
'''
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.'
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.'
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'
ret['result'] = False
ret['comment'] = msg
return ret
if policy_class and policy_class.lower() not in policy_classes:
msg = 'The policy_class parameter must be one of the following: {0}'
ret['result'] = False
ret['comment'] = msg
return ret
if not setting:
if computer_policy and user_policy:
policy_class = 'both'
elif computer_policy:
policy_class = 'machine'
elif user_policy:
policy_class = 'user'
if computer_policy and not isinstance(computer_policy, dict):
msg = 'The computer_policy must be specified as a dict.'
ret['result'] = False
ret['comment'] = msg
return ret
if user_policy and not isinstance(user_policy, dict):
msg = 'The user_policy must be specified as a dict.'
ret['result'] = False
ret['comment'] = msg
return ret
else:
user_policy = {}
computer_policy = {}
if policy_class.lower() == 'both':
user_policy[name] = setting
computer_policy[name] = setting
elif policy_class.lower() == 'user':
user_policy[name] = setting
elif policy_class.lower() == 'machine' or policy_class.lower() == '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': {}}
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)
if lookup['policy_found']:
pol_data[p_class]['policy_lookup'][policy_name] = lookup
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('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']]:
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)
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()
policies_are_equal = False
requested_policy_check = salt.utils.json.loads(requested_policy_json)
current_policy_check = salt.utils.json.loads(current_policy_json)
# Compared dicts, lists, and strings
if isinstance(requested_policy_check, six.string_types):
policies_are_equal = requested_policy_check == current_policy_check
elif isinstance(requested_policy_check, list):
policies_are_equal = salt.utils.data.compare_lists(
requested_policy_check,
current_policy_check
) == {}
elif isinstance(requested_policy_check, dict):
policies_are_equal = salt.utils.data.compare_dicts(
requested_policy_check,
current_policy_check
) == {}
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
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)
log.debug(
'we compared %s to %s',
requested_policy_json, current_policy_json
)
policy_changes.append(policy_name)
else:
if additional_policy_comments:
ret['comment'] = '"{0}" is already set ({1})\n'.format(policy_name, ', '.join(additional_policy_comments))
else:
ret['comment'] = '"{0}" is already set\n'.format(policy_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']
else:
policy_changes.append(policy_name)
log.debug('policy %s is not set, we will configure it',
policy_name)
if __opts__['test']:
if policy_changes:
ret['result'] = None
ret['comment'] = 'The following policies are set to change:\n{0}'.format(
'\n'.join(policy_changes))
else:
ret['comment'] = 'All specified policies are properly configured'
else:
if policy_changes:
_ret = __salt__['lgpo.set'](computer_policy=computer_policy,
user_policy=user_policy,
cumulative_rights_assignments=cumulative_rights_assignments,
adml_language=adml_language)
if _ret:
ret['result'] = _ret
ret['changes'] = salt.utils.dictdiffer.deep_diff(
current_policy,
__salt__['lgpo.get'](policy_class=policy_class,
adml_language=adml_language,
hierarchical_return=False))
if ret['changes']:
ret['comment'] = 'The following policies changed:\n{0}' \
''.format('\n'.join(policy_changes))
else:
ret['comment'] = 'The following policies are in the correct state:\n{0}' \
''.format('\n'.join(policy_changes))
else:
ret['result'] = False
ret['comment'] = 'Errors occurred while attempting to configure policies: {0}'.format(_ret)
return ret