diff --git a/README.md b/README.md index 63c9173..19fef53 100644 --- a/README.md +++ b/README.md @@ -51,6 +51,8 @@ The One Identity Safeguard Authentication Services Ansible Collection, referred * [`logon_policy_for_unix_host role`](roles/logon_policy_for_unix_host/README.md): Identifies the Active Directory users that have been explicitly granted log on permissions for the Unix hosts. +* [`logon_policy_for_ad_user role`](roles/logon_policy_for_ad_user/README.md): Identifies the hosts where Active Directory users have been granted log on permission. + ## Installation ### Prerequisites diff --git a/examples/README.md b/examples/README.md index 77a5699..5e551e7 100644 --- a/examples/README.md +++ b/examples/README.md @@ -70,3 +70,7 @@ The [`ad_group_conflicts`](run_ad_group_conflicts.yml) role example shows use of The [`logon_policy_for_unix_host`](run_logon_policy_for_unix_host.yml) role example shows use of the `logon_policy_for_unix_host` role in an Ansbile playbook. The variables most likely to be overriden have been included in this playbook for your convenience even though many are still set to their default values. +## `logon_policy_for_ad_user` Role Example + +The [`logon_policy_for_ad_user`](run_logon_policy_for_ad_user.yml) role example shows use of the `logon_policy_for_ad_user` role in an Ansbile playbook. The variables most likely to be overriden have been included in this playbook for your convenience even though many are still set to their default values. + diff --git a/examples/run_logon_policy_for_ad_user.yml b/examples/run_logon_policy_for_ad_user.yml new file mode 100644 index 0000000..9a9d7a1 --- /dev/null +++ b/examples/run_logon_policy_for_ad_user.yml @@ -0,0 +1,21 @@ +--- + +- hosts: all + gather_facts: false + + # The variables you would most likely want/need to override have been included + vars: + + # Report parameters + logon_policy_for_ad_user_user_name: '' + + # Facts + logon_policy_for_ad_user_facts_generate: true + + # Reports + logon_policy_for_ad_user_reports_generate: true + logon_policy_for_ad_user_reports_backup: false + + roles: + - name: oneidentity.authentication_services.logon_policy_for_ad_user + diff --git a/plugins/filter/logon_policy_for_ad_user_filters.py b/plugins/filter/logon_policy_for_ad_user_filters.py new file mode 100644 index 0000000..6612999 --- /dev/null +++ b/plugins/filter/logon_policy_for_ad_user_filters.py @@ -0,0 +1,104 @@ +#!/usr/bin/python +# -*- coding: utf-8 -*- + +# ------------------------------------------------------------------------------ +# Copyright (c) 2022, One Identity LLC +# File: logon_policy_for_ad_user_filters.py +# Desc: Ansible filters for logon_policy_for_ad_user role +# Auth: Laszlo Nagy +# Note: +# ------------------------------------------------------------------------------ + + +# ------------------------------------------------------------------------------ +# Imports +# ------------------------------------------------------------------------------ + +# Future module imports for consistency across Python versions +from __future__ import absolute_import, division, print_function + +# Want classes to be new type for consistency across Python versions +__metaclass__ = type + +from ansible.errors import AnsibleFilterError + + +# ------------------------------------------------------------------------------ +# Helper functions +# ------------------------------------------------------------------------------ + +# ------------------------------------------------------------------------------ +def get_logon_policy_for_ad_user(logon_policy_for_unix_hosts): + """ + Example of logon_policy_for_unix_hosts: + { + "192.168.56.101": [ + [ + "QASDEV\\eripley", + "VAS", + "1003", + "10001", + "Ellen Ripley", + "/home/eripley", + "/bin/bash" + ], + [ + "QASDEV\\smartbela", + "VAS", + "1371126438", + "1000", + "Bela Smart", + "/home/smartbela", + "/bin/bash" + ] + ], + "192.168.56.103": [ + [ + "QASDEV\\eripley", + "VAS", + "1003", + "10001", + "Ellen Ripley", + "/home/eripley", + "/bin/bash" + ], + [ + "QASDEV\\senior", + "VAS", + "1234567", + "1000", + "Senior", + "/home/Senior", + "/bin/bash" + ] + ] + } + """ + + users = {} + for host in logon_policy_for_unix_hosts: + users_allowed = logon_policy_for_unix_hosts[host] + for user in users_allowed: + if user[0] not in users: + users.update({user[0]: {'user': user, 'hosts': [host]} }) + else: + users[user[0]]['hosts'].append(host) + + return users + + +# ------------------------------------------------------------------------------ +# Classes +# ------------------------------------------------------------------------------ + +# ------------------------------------------------------------------------------ +class FilterModule(object): + """ + logon_policy_for_ad_user role jinja2 filters + """ + + def filters(self): + filters = { + 'logonpolicyforaduser': get_logon_policy_for_ad_user + } + return filters diff --git a/plugins/modules/get_logon_policy_for_unix_host.py b/plugins/modules/get_logon_policy_for_unix_host.py index 7041524..9c1abc5 100644 --- a/plugins/modules/get_logon_policy_for_unix_host.py +++ b/plugins/modules/get_logon_policy_for_unix_host.py @@ -34,6 +34,12 @@ vastool list users-allowed command. options: + user_name: + description: + - Return only the specified user or all users? + type: str + required: false + default: '' facts: description: - Generate Ansible facts? @@ -110,6 +116,7 @@ # ------------------------------------------------------------------------------ # Arg choices and defaults +USER_NAME_DEFAULT = '' FACTS_DEFAULT = True FACTS_VERBOSE_DEFAULT = True FACTS_KEY_DEFAULT = 'get_logon_policy_for_unix_host_facts_key' @@ -127,6 +134,11 @@ def run_module(): # Module argument info module_args = { + 'user_name': { + 'type': 'str', + 'required': False, + 'default': USER_NAME_DEFAULT + }, 'facts': { 'type': 'bool', 'required': False, @@ -178,6 +190,7 @@ def run_normal(params, result): users_allowed = [] # Parameters + user_name = params['user_name'] if params['user_name'] else USER_NAME_DEFAULT facts = params['facts'] facts_key = params['facts_key'] if params['facts_key'] else FACTS_KEY_DEFAULT @@ -190,6 +203,9 @@ def run_normal(params, result): if err is None: err, users_allowed = run_vastool_list_users_allowed() + if user_name: + users_allowed = [user for user in users_allowed if user[0] == user_name] + except Exception: tb = traceback.format_exc() err = str(tb) diff --git a/roles/logon_policy_for_ad_user/README.md b/roles/logon_policy_for_ad_user/README.md new file mode 100644 index 0000000..6c2214d --- /dev/null +++ b/roles/logon_policy_for_ad_user/README.md @@ -0,0 +1,97 @@ +# `logon_policy_for_ad_user` Role + +The `logon_policy_for_ad_user` role creates CSV and HTML reports that list the hosts where Active Directory users have been granted log on permission. + +## Variables + +All of the variables shown below have a default value but can be overridden to suit your environment. Variable overriding can be done in playbooks, inventories, from the command line using the `-e` switch with the `ansible-playbook` command, or from Ansible Tower and AWX. See [Ansbile documentation](https://docs.ansible.com/ansible/latest/user_guide/playbooks_variables.html) for further information. + +### Report parameters + +* `logon_policy_for_ad_user_user_name`: An Active Directory user can be specified in the following format: DOMAIN\SamAccountName. For example: EXAMPLE\jason. Reports will be generated for the specified user. If no user is specified then the reports will be generated for all Active Directory users who have been granted log on permission. + + Default value is: + ```yaml + logon_policy_for_ad_user_user_name: '' + ``` + +### Facts generation + +Facts generation variable defaults for all roles are set by variables in the [`common`](../common/README.md) role and can be overriden for all roles by setting the appropriate [`common`](../common/README.md) role variable. See [common role facts generation variables](../common/README.md#facts-generation) in the [`common`](../common/README.md) role. + +* `logon_policy_for_ad_user_facts_generate` enables facts generation. Implicitely enabled if `logon_policy_for_ad_user_reports_generate` is set. + + Default value is: + ```yaml + logon_policy_for_ad_user_facts_generate: "{{ facts_generate }}" + ``` + +### Report generation + +Report generation variable defaults for all roles are set by variables in the [`common`](../common/README.md) role and can be overriden for all roles by setting the appropriate [`common`](../common/README.md) role variable. See [common role report generation variables](../common/README.md#report-generation) in the [`common`](../common/README.md) role. + +* `logon_policy_for_ad_user_reports_generate` enables report generation. Reports are generated at the end of a `logon_policy_for_ad_user` run for all hosts. + + Default value is: + ```yaml + logon_policy_for_ad_user_reports_generate: "{{ reports_generate }}" + ``` + +* `logon_policy_for_ad_user_reports_backup` enables backup of prior reports by renaming them with the date and time they were generated so that the latest reports do not override the previous reports. + + Default value is: + ```yaml + logon_policy_for_ad_user_reports_backup: "{{ reports_backup }}" + + ``` + +* `logon_policy_for_ad_user_reports_host` sets the host on which the reports should be generated. + + Default value is: + ```yaml + logon_policy_for_ad_user_reports_host: "{{ reports_host }}" + ``` + +* `logon_policy_for_ad_user_reports` is a list of dictionaries that define the reports to be generated. The default value creates a CSV and HTML report using the templates included with the `logon_policy_for_ad_user` role. + + Default value is: + ```yaml + logon_policy_for_ad_user_reports: + - src: logon_policy_for_ad_user_report.csv.j2 + dest: logon_policy_for_ad_user_report.csv + - src: logon_policy_for_ad_user_report.html.j2 + dest: logon_policy_for_ad_user_report.html + ``` + + The `src` key for each list entry is the report template file on the Ansible control node. With a relative path Ansible will look in the `logon_policy_for_ad_user` role `template` directory. Use a absolute path to speciy templates located elsewhere on the Ansible control node. + + The `dest` key for each list entry is the report file on the machine specified in `logon_policy_for_ad_user_reports_host`. If `logon_policy_for_ad_user_reports_host` is set to the Ansible control node a relative path can be used and it will be relative to the directory from which the playbook is run. For other hosts, an absolute path must be used. In either case the containing directory must exist. + +## Usage + +Below is a sample playbook using the `logon_policy_for_ad_user` role. + +```yaml +--- + +- hosts: all + gather_facts: false + + # The variables you would most likely want/need to override have been included + vars: + + # Report parameters + logon_policy_for_ad_user_user_name: '' + + # Facts + logon_policy_for_ad_user_facts_generate: true + + # Reports + logon_policy_for_ad_user_reports_generate: true + logon_policy_for_ad_user_reports_backup: false + + roles: + - name: oneidentity.authentication_services.logon_policy_for_ad_user +``` + +For a copy of this and other sample playbooks see [examples](../../examples/README.md) diff --git a/roles/logon_policy_for_ad_user/defaults/main.yml b/roles/logon_policy_for_ad_user/defaults/main.yml new file mode 100644 index 0000000..b4a3013 --- /dev/null +++ b/roles/logon_policy_for_ad_user/defaults/main.yml @@ -0,0 +1,38 @@ +--- + +# Report parameters +# ------------------------------------------------------------------------------ + +logon_policy_for_ad_user_user_name: '' + + +# Facts settings +# ------------------------------------------------------------------------------ + +logon_policy_for_ad_user_facts_generate: "{{ facts_generate }}" + + +# Reports settings +# ------------------------------------------------------------------------------ + +logon_policy_for_ad_user_reports_generate: "{{ reports_generate }}" +logon_policy_for_ad_user_reports_backup: "{{ reports_backup }}" + +# On which host should the reports be generated. +# TODO: This has only been tested on the Ansible control node (127.0.0.1) +logon_policy_for_ad_user_reports_host: "{{ reports_host }}" + +# List of reports to generate +# src: Is the report template file on the Ansible control node. +# With no or relative path Ansible will look in the logon_policy_for_ad_user role template directory. +# Full path to find the template files elsewhere on the Ansible control node. +# dest: Is the destination file on the host (logon_policy_for_ad_user_reports_host.) +# With no or relative path when the destination is the Ansible control node. +# (logon_policy_for_ad_user_reports_host = 127.0.0.1) relative to the playbook directory. +# Full path for other locations or on other hosts. +# In either case the directory must already exist. +logon_policy_for_ad_user_reports: + - src: logon_policy_for_ad_user_report.csv.j2 + dest: logon_policy_for_ad_user_report.csv + - src: logon_policy_for_ad_user_report.html.j2 + dest: logon_policy_for_ad_user_report.html diff --git a/roles/logon_policy_for_ad_user/meta/main.yml b/roles/logon_policy_for_ad_user/meta/main.yml new file mode 100644 index 0000000..cd76742 --- /dev/null +++ b/roles/logon_policy_for_ad_user/meta/main.yml @@ -0,0 +1,66 @@ +--- + +galaxy_info: + + author: Laszlo Nagy + + company: One Identity + + description: > + Identifies the hosts where Active Directory users have been granted log on permission. + + issue_tracker_url: https://github.com/OneIdentity/ansible-authentication-services/issues + + license: Apache-2.0 + + min_ansible_version: 2.9 + + # + # To view available platforms and versions (or releases), visit: + # https://galaxy.ansible.com/api/v1/platforms/ + # + platforms: + - name: Amazon + versions: + - all + - name: MacOSX + versions: + - 10.12 + - 10.13 + - 10.14 + - 10.15 + - name: EL + versions: + - all + - name: Debian + versions: + - all + - name: Fedora + versions: + - all + - name: FreeBSD + versions: + - all + - name: AIX + versions: + - 7.1 + - 7.2 + - name: opensuse + versions: + - all + - name: Solaris + versions: + - all + - name: SLES + versions: + - 11 + - 12 + - 15 + - name: Ubuntu + versions: + - all + + # galaxy_tags: [] + +dependencies: + - role: common diff --git a/roles/logon_policy_for_ad_user/tasks/gather_facts.yml b/roles/logon_policy_for_ad_user/tasks/gather_facts.yml new file mode 100644 index 0000000..70522e3 --- /dev/null +++ b/roles/logon_policy_for_ad_user/tasks/gather_facts.yml @@ -0,0 +1,24 @@ +--- + +# Gather facts +- name: gather host information + setup: + register: result + ignore_errors: true + ignore_unreachable: true + +- name: fail when unreachable + fail: + msg: "{{ result.msg }}" + when: result.unreachable is defined and result.unreachable == true + +# MODULE FAILURE\nSee stdout/stderr for the exact error +- name: fail when module failure + fail: + msg: "{{ result.module_stderr + result.module_stdout }}" + register: result + when: result.msg is defined and "'See stdout/stderr for the exact error' in result.msg" + +- fail: + msg: "{{ result.msg }}" + when: result.msg is defined diff --git a/roles/logon_policy_for_ad_user/tasks/generate_reports.yml b/roles/logon_policy_for_ad_user/tasks/generate_reports.yml new file mode 100644 index 0000000..f5af2c4 --- /dev/null +++ b/roles/logon_policy_for_ad_user/tasks/generate_reports.yml @@ -0,0 +1,11 @@ +--- + +- name: generate reports + template: + src: "{{ item.src }}" + dest: "{{ item.dest }}" + backup: "{{ logon_policy_for_ad_user_reports_backup }}" + with_items: "{{ logon_policy_for_ad_user_reports }}" + delegate_to: "{{ logon_policy_for_ad_user_reports_host }}" + run_once: true + changed_when: false diff --git a/roles/logon_policy_for_ad_user/tasks/get_logon_policy_for_ad_user.yml b/roles/logon_policy_for_ad_user/tasks/get_logon_policy_for_ad_user.yml new file mode 100644 index 0000000..92abf93 --- /dev/null +++ b/roles/logon_policy_for_ad_user/tasks/get_logon_policy_for_ad_user.yml @@ -0,0 +1,25 @@ +--- + +- name: Get logon policy for ad user + get_logon_policy_for_unix_host: + user_name: "{{ logon_policy_for_ad_user_user_name }}" + facts: "{{ logon_policy_for_ad_user_facts_generate or logon_policy_for_ad_user_reports_generate }}" + facts_key: sas_logon_policy_for_ad_user_key + register: result + failed_when: false + +# Fail if there was a message returned +- fail: + msg: "{{ result.msg }}" + when: result.msg + +- set_fact: + logon_policy_for_ad_user: | + {%- set logon_policy_for_unix_hosts = {} %} + {%- for host in play_hosts %} + {%- if logon_policy_for_unix_hosts.update({host: (hostvars[host].ansible_facts.sas_logon_policy_for_ad_user_key.users_allowed | default([])) }) %} + {%- endif %} + {%- endfor %} + {{ logon_policy_for_unix_hosts | oneidentity.authentication_services.logonpolicyforaduser() }} + run_once: true + diff --git a/roles/logon_policy_for_ad_user/tasks/main.yml b/roles/logon_policy_for_ad_user/tasks/main.yml new file mode 100644 index 0000000..4c202f9 --- /dev/null +++ b/roles/logon_policy_for_ad_user/tasks/main.yml @@ -0,0 +1,33 @@ +--- + +# ------------------------------------------------------------------------------ +# Main +# ------------------------------------------------------------------------------ + +- block: + + # Gather facts + - include_tasks: gather_facts.yml + + # Gather hosts where AD users have been granted log on permission + - include_tasks: get_logon_policy_for_ad_user.yml + + # We get here on success + - include_tasks: utils/set_fact_success.yml + + ignore_unreachable: true + + rescue: + + # Set unreachable status and fail + - include_tasks: utils/set_fact_unreachable.yml + when: result.unreachable is defined and result.unreachable == true + + # Set fail status and fail + - include_tasks: utils/set_fact_failed.yml + + always: + + # Generate reports + - include_tasks: generate_reports.yml + when: logon_policy_for_ad_user_reports_generate diff --git a/roles/logon_policy_for_ad_user/tasks/utils/set_fact_failed.yml b/roles/logon_policy_for_ad_user/tasks/utils/set_fact_failed.yml new file mode 100644 index 0000000..aeb6e8d --- /dev/null +++ b/roles/logon_policy_for_ad_user/tasks/utils/set_fact_failed.yml @@ -0,0 +1,14 @@ +--- + +# Set failed +- set_fact: + cacheable: true + sas_logon_policy_for_ad_user: + unreachable: false + failed: true + changed: false + when: logon_policy_for_ad_user_reports_generate or logon_policy_for_ad_user_facts_generate + +# Propogate fail +- fail: + msg: "{{ result.msg | default('Unexpected error') }}" diff --git a/roles/logon_policy_for_ad_user/tasks/utils/set_fact_success.yml b/roles/logon_policy_for_ad_user/tasks/utils/set_fact_success.yml new file mode 100644 index 0000000..32746f3 --- /dev/null +++ b/roles/logon_policy_for_ad_user/tasks/utils/set_fact_success.yml @@ -0,0 +1,10 @@ +--- + +# Set success +- set_fact: + cacheable: true + sas_logon_policy_for_ad_user: + unreachable: false + failed: false + changed: false + when: logon_policy_for_ad_user_reports_generate or logon_policy_for_ad_user_facts_generate diff --git a/roles/logon_policy_for_ad_user/tasks/utils/set_fact_unreachable.yml b/roles/logon_policy_for_ad_user/tasks/utils/set_fact_unreachable.yml new file mode 100644 index 0000000..75ebd86 --- /dev/null +++ b/roles/logon_policy_for_ad_user/tasks/utils/set_fact_unreachable.yml @@ -0,0 +1,14 @@ +--- + +# Set unreachable +- set_fact: + cacheable: true + sas_logon_policy_for_ad_user: + unreachable: true + failed: true + changed: false + when: logon_policy_for_ad_user_reports_generate or logon_policy_for_ad_user_facts_generate + +# Propogate fail +- fail: + msg: "{{ result.msg | default('Unexpected error') }}" diff --git a/roles/logon_policy_for_ad_user/templates/Logo_2020-OneIdentity_FullColor_Horizontal.png b/roles/logon_policy_for_ad_user/templates/Logo_2020-OneIdentity_FullColor_Horizontal.png new file mode 100644 index 0000000..d95f3f3 Binary files /dev/null and b/roles/logon_policy_for_ad_user/templates/Logo_2020-OneIdentity_FullColor_Horizontal.png differ diff --git a/roles/logon_policy_for_ad_user/templates/Logo_2020-OneIdentity_FullColor_Horizontal.txt b/roles/logon_policy_for_ad_user/templates/Logo_2020-OneIdentity_FullColor_Horizontal.txt new file mode 100644 index 0000000..128f2ad --- /dev/null +++ b/roles/logon_policy_for_ad_user/templates/Logo_2020-OneIdentity_FullColor_Horizontal.txt @@ -0,0 +1,249 @@ +iVBORw0KGgoAAAANSUhEUgAABWsAAAGQCAYAAADYyjIgAAAAGXRFWHRTb2Z0d2FyZQBBZG9iZSBJ +bWFnZVJlYWR5ccllPAAANv5JREFUeNrs3U9y01jbN2A9b2X+5F1BuyeZdlgBZgWEoiozimQFwAoC +KyCsIKaYpYoivQLMCjo9zaT9rODNM0/V9+mOT7pDCCDZsnQkXVeVygkotnz09/x8fKsoAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAGjAvzQBLG2dXuyWD9sVZl1c7e8stBgAAAAATRLWMni3Qthp+qeH6XGSpnWd +l9Nlmv5Mj/FvQl0AAAAAKhPWMhhbpxcRyEYwOy2n34plELubwaLNy2lRLIPc86v9nbm1BQAAAMBd +wlp6a+v0YlIsg9mH6XHSo8WPkbfzcvoSj1f7O5fWKAAAAMC4CWvpjTRydlpOj4v+hbM/cxPe/m7k +LQAAAMA4CWvJ2q3RsxHQ7o3kbcco27NiGdye2QoAAAAAxkFYS3bSCNoIZscU0H6P4BYAAABgJIS1 +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 +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAABir/y/AAPqmv1QA3VFPAAAAAElFTkSuQmCC diff --git a/roles/logon_policy_for_ad_user/templates/logon_policy_for_ad_user_report.csv.j2 b/roles/logon_policy_for_ad_user/templates/logon_policy_for_ad_user_report.csv.j2 new file mode 100644 index 0000000..a2b7dc9 --- /dev/null +++ b/roles/logon_policy_for_ad_user/templates/logon_policy_for_ad_user_report.csv.j2 @@ -0,0 +1,21 @@ +Logon Policy For AD User,,{{ now(fmt='%Y-%m-%d %H:%M:%S') }},,,,,, + +Active Directory User,System Name,IP Address,OS Distro,OS Version,UID,GID,GECOS,Home,Shell + +{% for policy in logon_policy_for_ad_user | sort %} +{% for host in logon_policy_for_ad_user[policy]['hosts'] | sort %} +{{ '"%s","%s","%s","%s","%s","%s","%s","%s","%s","%s"' | format( +logon_policy_for_ad_user[policy]['user'][0], +host, +hostvars[host]['ansible_facts']['default_ipv4']['address'] | default(), +hostvars[host]['ansible_facts']['distribution'] | default(), +hostvars[host]['ansible_facts']['distribution_version'] | default(), +logon_policy_for_ad_user[policy]['user'][2], +logon_policy_for_ad_user[policy]['user'][3], +logon_policy_for_ad_user[policy]['user'][4], +logon_policy_for_ad_user[policy]['user'][5], +logon_policy_for_ad_user[policy]['user'][6] +)}} +{% endfor %} +{% endfor %} + diff --git a/roles/logon_policy_for_ad_user/templates/logon_policy_for_ad_user_report.html.j2 b/roles/logon_policy_for_ad_user/templates/logon_policy_for_ad_user_report.html.j2 new file mode 100644 index 0000000..13094d2 --- /dev/null +++ b/roles/logon_policy_for_ad_user/templates/logon_policy_for_ad_user_report.html.j2 @@ -0,0 +1,45 @@ +{# Imports #} +{% import 'logon_policy_for_ad_user_report_common.html.j2' as report with context %} + +{# Globals #} +{% set report_name = 'Logon Policy For AD User Report' %} + +{# HTML head #} +{{ report.head(report_name) }} + +{# HTML body #} + + + {# Create overall page container #} +
+ + {# Title #} + {{ report.title(report_name) }} + + {# Data table #} + {{ report.table() }} + + {# Footer #} + {{ report.footer(collection_version, now(fmt='%Y-%m-%d %H:%M:%S')) }} + + {# JS libraries #} + {{ report.libraries() }} + + {# Table data #} + {{ report.table_data() }} + + {# Table detail formatter #} + {{ report.table_detail_formatter() }} + + {# Table detail copy tooltip #} + {{ report.table_detail_tooltip() }} + + {# Table cell style #} + {{ report.table_cell_style() }} + +
+ + + +{# HTML end #} +{{ report.end() }} diff --git a/roles/logon_policy_for_ad_user/templates/logon_policy_for_ad_user_report_common.html.j2 b/roles/logon_policy_for_ad_user/templates/logon_policy_for_ad_user_report_common.html.j2 new file mode 100644 index 0000000..9ccc8ec --- /dev/null +++ b/roles/logon_policy_for_ad_user/templates/logon_policy_for_ad_user_report_common.html.j2 @@ -0,0 +1,242 @@ +{# Header #} +{% macro head(report_name) %} + + + + + {# Required meta tags for Bootstrap #} + + + + {# CSS libraries include at end of head per best practices #} + + + + + {# Ionicons preferred method of inclusion #} + + + + Safeguard Authentication Services: {{ report_name }} + +{% endmacro %} + +{# Title #} +{% macro title(report_name) %} +
+ + + {# Import base-64-encoded logo and embed in doc #} + {% import 'Logo_2020-OneIdentity_FullColor_Horizontal.txt' as logo %} + One Identity + + Safeguard Authentication Services {{ report_name }} + +
+The Logon Policy for AD User report is used to identify the hosts where one or more AD Users have been granted logon permission. +
+{% if logon_policy_for_ad_user_user_name %} +Selected AD User: {{logon_policy_for_ad_user_user_name}}
+{% else %} +Selected AD User: All
+{% endif %} +
+{% endmacro %} + + +{# Table #} +{% macro table() %} + + + + + + + + + + + + + +
IdNumber of HostsUsernameUser IDGroup IDCommentHome DirectoryLogin Shell
+ +{% for policy in logon_policy_for_ad_user | sort %} + + + + + + + + + + + +
HostnameGroupIP AddressOS DistroOS VersionHW Arch
+{% endfor %} +{% endmacro %} + +{# Footer #} +{% macro footer(report_version, report_time) %} + +

Generated: {{ report_time }}

+

Version: {{ report_version }}

+
+{% endmacro %} + +{# JS libraries #} +{% macro libraries() %} + + + + + +{% endmacro %} + +{# Table data #} +{% macro table_data() %} + +{% endmacro %} + +{# Table detail formatter #} +{% macro table_detail_formatter() %} + +{% endmacro %} + +{# Table detail copy tooltip #} +{% macro table_detail_tooltip() %} + +{% endmacro %} + +{# Table cell style #} +{% macro table_cell_style() %} + +{% endmacro %} + +{# HTML end #} +{% macro end() %} + +{% endmacro %} diff --git a/roles/logon_policy_for_ad_user/vars/main.yml b/roles/logon_policy_for_ad_user/vars/main.yml new file mode 100644 index 0000000..ed97d53 --- /dev/null +++ b/roles/logon_policy_for_ad_user/vars/main.yml @@ -0,0 +1 @@ +---